Mysql
 sql >> Datenbank >  >> RDS >> Mysql

SQL-Abfrage mehrerer Tabellen mit mehreren Verknüpfungen und Spaltenfeld mit kommagetrennter Liste

Wenn Sie die Tabellenstruktur wirklich nicht ändern können, ist wahrscheinlich das Beste, was Sie tun können, einer der alten Listen-Hacks:

  • Verwenden Sie ein JOIN mit 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 ;

  • Verwenden Sie LIKE um das Vorhandensein eines bestimmten serviceID-Werts in der Knotenliste zu erkennen

    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

Wie Sie jedoch bereits bemerkt haben, sollte diese Spalte wirklich normalisiert werden. Während die obigen Methoden für kleine Datensätze funktionieren sollten, leiden sie unter den üblichen Problemen beim Arbeiten mit "Listen". Keine der beiden Methoden ist sehr indexfreundlich und lässt sich daher nicht gut skalieren. Außerdem führen beide Zeichenfolgenvergleiche durch. Der kleinste Unterschied kann also dazu führen, dass der Abgleich fehlschlägt. Beispiel:1,4 würde zwei serviceIDs entsprechen, wobei 1,(space)4 oder 1,4.0 würde nur mit einem übereinstimmen.

Aktualisierung basierend auf Kommentaren:

Beim zweiten Lesen bin ich mir nicht sicher, ob das Obige genau die Frage beantwortet, die Sie stellen, aber es sollte eine gute Grundlage bieten, um damit zu arbeiten ...

Wenn Sie keine CSV-Liste mehr möchten, verwenden Sie einfach eine der obigen Abfragen und geben Sie die einzelnen Abfragespalten wie gewohnt aus. Das Ergebnis ist ein Dienstname pro Zeile, dh:

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

Andernfalls, wenn Sie die durch Kommas getrennten Werte beibehalten müssen, besteht eine Möglichkeit darin, einen <cfoutput group=".."> zu verwenden auf die Abfrageergebnisse. Da die Ergebnisse zuerst nach "Host" geordnet werden, etwa wie im folgenden Code. Hinweis: Damit "Gruppe" ordnungsgemäß funktioniert, müssen die Ergebnisse nach Host sortiert werden und Sie müssen mehrere cfoutput verwenden Tags wie unten gezeigt.

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

Das Ergebnis sollte so aussehen:

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, 


Aktualisierung 2:

Ich habe vergessen, dass es eine einfachere Alternative zu cfoutput group gibt 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>