Mysql
 sql >> Base de données >  >> RDS >> Mysql

Interrogation SQL sur plusieurs tables, avec plusieurs jointures et champ de colonne avec une liste séparée par des virgules

Si vous ne pouvez vraiment pas modifier la structure du tableau, le mieux que vous puissiez faire est probablement l'un des anciens hacks de liste :

  • Utiliser un JOIN avec 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 ;

  • Utilisez LIKE pour détecter la présence d'une valeur serviceID spécifique dans la liste des nœuds

    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

Cependant, comme vous l'avez déjà noté, cette colonne devrait vraiment être normalisée. Alors que les méthodes ci-dessus devraient fonctionner pour de petits ensembles de données, elles souffrent des problèmes habituels de travail avec des "listes". Aucune des deux méthodes n'est très conviviale pour les index et, par conséquent, ne s'adaptera pas bien. En outre, les deux effectuent des comparaisons de chaînes. Ainsi, la moindre différence peut entraîner l'échec de la correspondance. Par exemple, 1,4 correspondrait à deux serviceID, alors que 1,(space)4 ou 1,4.0 correspondrait à un seul.

Mise à jour basée sur les commentaires :

En deuxième lecture, je ne suis pas sûr que ce qui précède réponde à la question précise que vous posez, mais cela devrait fournir une bonne base de travail avec ...

Si vous ne voulez plus de liste CSV, utilisez simplement l'une des requêtes ci-dessus et sortez les colonnes de requête individuelles comme d'habitude. Le résultat sera un nom de service par ligne, c'est-à-dire :

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

Sinon, si vous devez conserver les valeurs séparées par des virgules, une possibilité consiste à utiliser un <cfoutput group=".."> sur les résultats de la requête. Étant donné que les résultats sont d'abord classés par "hôte", quelque chose comme le code ci-dessous. NB : Pour que "groupe" fonctionne correctement, les résultats doivent être triés par Host et vous devez utiliser plusieurs cfoutput balises comme indiqué ci-dessous.

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

Le résultat devrait ressembler à ceci :

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, 


Mise à jour 2 :

J'ai oublié qu'il existe une alternative plus simple au cfoutput group dans 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>