SQL query multiple tables, with multiple joins and column field with comma separated list

后端 未结 1 1873
南旧
南旧 2020-12-07 01:51

I have a query where I join three separate tables (node, control, service).

Below is their column headings and sample data.

NODE TABLE  (contains ove         


        
相关标签:
1条回答
  • 2020-12-07 01:54

    If you really cannot modify the table structure, probably the best you can do is one of the old list hacks:

    • Use a JOIN with 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 to detect the presence of a specific serviceID value within the node list

      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

    However, as you already noted that column really should be normalized. While the methods above should work for small data sets, they suffer from the usual problems of working with "lists". Neither method is very index friendly, and as a result, will not scale well. Also, both perform string comparisons. So the slightest difference may cause the matching to fail. For example, 1,4 would match two serviceID's, whereas 1,(space)4 or 1,4.0 would match only one.

    Update based on comments:

    On second read, I am not sure the above answers the precise question you are asking, but it should provide a good basis to work with ...

    If you no longer want a CSV list, just use one of the queries above and output the individual query columns as usual. The result will be one service name per row, ie:

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

    Otherwise, if you need to preserve the comma separated values, one possibility is to use a <cfoutput group=".."> on the query results. Since the results are ordered by "Host" first, something like the code below. NB: For "group" to work properly, the results must be ordered by Host and you must use multiple cfoutput tags as shown below.

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

    The result should look like this:

    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, 
    


    Update 2:

    I forgot there is a simpler alternative to 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>
    
    0 讨论(0)
提交回复
热议问题