Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

SQL consulta várias tabelas, com várias junções e campo de coluna com lista separada por vírgulas


Se você realmente não pode modificar a estrutura da tabela, provavelmente o melhor que você pode fazer é um dos antigos hacks de lista:

  • Use um JOIN com 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 ;

  • Use LIKE para detectar a presença de um valor de serviceID específico na lista de nós

    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

No entanto, como você já observou, essa coluna realmente deve ser normalizada. Embora os métodos acima devam funcionar para pequenos conjuntos de dados, eles sofrem com os problemas usuais de trabalhar com "listas". Nenhum dos métodos é muito amigável ao índice e, como resultado, não será bem dimensionado. Além disso, ambos realizam comparações de strings. Portanto, a menor diferença pode fazer com que a correspondência falhe. Por exemplo, 1,4 corresponderia a dois serviceIDs, enquanto 1,(space)4 ou 1,4.0 corresponderia a apenas um.

Atualização com base nos comentários:

Na segunda leitura, não tenho certeza se a resposta acima responde à pergunta exata que você está fazendo, mas deve fornecer uma boa base para trabalhar ...

Se você não quiser mais uma lista CSV, basta usar uma das consultas acima e gerar as colunas de consulta individuais como de costume. O resultado será um nome de serviço por linha, ou seja:
   server1 | Control Name One | Service Name 200
   server1 | Control Name One | Service Name 50
   ..

Caso contrário, se você precisar preservar os valores separados por vírgulas, uma possibilidade é usar um <cfoutput group=".."> nos resultados da consulta. Como os resultados são ordenados por "Host" primeiro, algo como o código abaixo. NB: Para que o "grupo" funcione corretamente, os resultados devem ser ordenados por Host e você deve usar vários cfoutput etiquetas como mostrado abaixo.
 <cfoutput query="..." group="Host"> 
    #Host# |
    #ControlName# |
    <cfoutput>
      #ServiceName#,
    </cfoutput>
    <br>
 </cfoutput>

O resultado deve ficar assim:
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, 




Atualização 2:

Esqueci que existe uma alternativa mais simples para cfoutput group no 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>