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
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>