Mysql
 sql >> Database >  >> RDS >> Mysql

SQL interroga più tabelle, con più join e campo colonna con elenco separato da virgole

Se davvero non puoi modificare la struttura della tabella, probabilmente il meglio che puoi fare è uno dei vecchi hack delle liste:

  • Usa un JOIN con FIND_IN_SET(value, commaSeparatedString)

    SELECT n.Host, c.Name AS ControlName, s.Name AS ServiceName FROM node n LEFT JOIN control c ON c.controlID = n.controlID LEFT JOIN service s ON FIND_IN_SET(s.serviceID, n.serviceId) ORDER BY n.host, s.Name ;

  • Usa LIKE per rilevare la presenza di un valore ServiceID specifico all'interno dell'elenco dei nodi

    SELECT n.Host, c.Name AS ControlName, s.Name AS ServiceName FROM node n LEFT JOIN control c ON c.controlID = n.controlID LEFT JOIN service s ON CONCAT(',', n.serviceID,',') LIKE CONCAT('%,', s.serviceID,',%') ORDER BY n.host, s.Name ;

SQLFiddle

Tuttavia, come hai già notato, quella colonna dovrebbe davvero essere normalizzata. Mentre i metodi sopra dovrebbero funzionare per piccoli set di dati, soffrono dei soliti problemi di lavorare con "liste". Nessuno dei due metodi è molto compatibile con gli indici e, di conseguenza, non si ridimensiona bene. Inoltre, entrambi eseguono confronti di stringhe. Quindi la minima differenza potrebbe far fallire la corrispondenza. Ad esempio, 1,4 corrisponderebbe a due serviceID, mentre 1,(space)4 o 1,4.0 corrisponderebbe solo a uno.

Aggiornamento basato sui commenti:

In seconda lettura, non sono sicuro che quanto sopra risponda alla domanda precisa che stai ponendo, ma dovrebbe fornire una buona base per lavorare con ...

Se non desideri più un elenco CSV, utilizza semplicemente una delle query precedenti e genera come al solito le singole colonne di query. Il risultato sarà un nome di servizio per riga, ovvero:

   server1 | Control Name One | Service Name 200
   server1 | Control Name One | Service Name 50
   ..

In caso contrario, se è necessario preservare i valori separati da virgola, una possibilità è utilizzare un <cfoutput group=".."> sui risultati della query. Poiché i risultati sono ordinati prima per "Host", qualcosa come il codice seguente. NB: Affinché "gruppo" funzioni correttamente, i risultati devono essere ordinati da Host e devi usare più cfoutput tag come mostrato di seguito.

 <cfoutput query="..." group="Host"> 
    #Host# |
    #ControlName# |
    <cfoutput>
      #ServiceName#,
    </cfoutput>
    <br>
 </cfoutput>

Il risultato dovrebbe assomigliare a questo:

server1 | Control Name One | Service Name 200, Service Name 50, Service Name Four, Service Name One, Service Name Three, Service Name Two, 
server2 | Control Name Two | Service Name 200, Service Name Four, Service Name Three, Service Name Two, 
server3 | Control Name Two | Service Name 200, Service Name 50, Service Name Four, Service Name One, Service Name Three, Service Name Two, 
server4 | Control Name Three | Service Name 200, Service Name 50, Service Name One, Service Name Two, 
server5 | Control Name Three | Service Name Four, Service Name One, 


Aggiornamento 2:

Dimenticavo che esiste un'alternativa più semplice a cfoutput group in MySQL:GROUP_CONCAT

<cfquery name="qry" datasource="MySQL5">
   SELECT n.Host, c.Name AS ControlName, GROUP_CONCAT(s.Name) AS ServiceNameList 
   FROM node n 
        LEFT JOIN control c ON c.controlID = n.controlID 
        LEFT JOIN service s ON FIND_IN_SET(s.serviceID, n.serviceId) 
   GROUP BY n.Host, c.Name
   ORDER BY n.host
</cfquery>