我有一个查询,我连接三个单独的表(节点,控制,服务)。
以下是他们的列标题和样本数据。
NODE TABLE (contains over 7000 rows)
nodeID | host | serviceID | controlID
1 | server1 | 1,2,3,4,9,50,200 | 1
2 | server2 | 2,3,4,9,200 | 2
3 | server3 | 1,2,3,4,9,50,200 | 2
4 | server4 | 1,2,50,200 | 3
5 | server5 | 1,4 | 3
CONTROL TABLE (contains roughly 50 rows)
controlID | name
1 | Control Name One
2 | Control Name Two
3 | Control Name Three
4 | Control Name Four
5 | Control Name Five
SERVICE TABLE (contains roughly 3000 rows)
serviceID | name
1 | Service Name One
2 | Service Name Two
3 | Service Name Three
4 | Service Name Four
5 | Service Name Five
6 | Service Name Six
50 | Service Name 50
200 | Service Name 200
如您所见,除 node.serviceID 列外,数据库表有一些规范化。我完全同意应该规范化 node.serviceID 并创建一对多的数据透视表。那里没有争论。但是,我不控制将信息插入数据库的脚本。我只能从表中读取数据并将数据格式化。
因此,下面是我编写的SQL查询确实有效,但正如预期的那样, node.serviceID 与 service.serviceID 无法很好地结合。请注意,我在最终查询中没有使用SELECT *,我从节点表中选择了大约20个字段,并且不希望使查询更加混乱。以下只是一个例子。
SELECT *
FROM node AS a
LEFT JOIN control AS b ON a.controlID = b.controlid
LEFT JOIN service AS c ON a.serviceID = c.serviceId
ORDER BY a.host
上面的查询吐出了类似的东西:
Host Control Services
server1 Control Name One 1,2,3,4,9,50
server2 Control Name Three 1,2,9,50
server3 Control Name Two 4
server4 Control Name Four 1,2,3,4,9
server5 Control Name Two 1,2,3,50
server6 Control Name Five 1,3,4,9,50
我正在寻找的是:
Host Control Services
server1 Control Name One Service Name One,
Service Name Two,
Service Name Three,
Service Name Four,
Service Name Nine,
Service Name Fifty
server2 Control Name Three Service Name One,
Service Name Two,
Service Name Nine,
Service Name Fifty
server3 Control Name Two Service Name Four
server4 Control Name Four Service Name One,
Service Name Two,
Service Name Three,
Service Name Four,
Service Name Nine
我已经搜索了有这样一个问题的人的stackoverflow.com,但我只能找到在ID和名称上加入多个表,或者有人在扩展ID列表但不能同时扩展这两个表。
这一次接近:Using id that are comma separated sql但不完全。
我已经尝试了各种带有ListToArray()的CFML方法,并尝试使用索引对它们进行循环,但没有任何方法可以帮助我。
我抓住数据的服务器是MySQL 5.1,我使用jQuery和ColdFusion(Railo 4.2)的组合来格式化数据。
这是我第一次在stackoverflow上发帖,所以我很抱歉,如果确实有这个问题的答案,我搜索的时间不长,并且会使这个问题重复。
-----------------更新--------------------
我尝试了Leigh建议的查询和CFML。
所以,我得到以下内容:
server1服务名称一,服务名一,服务名一,服务名一,服务名一,服务名一,服务名二,服务名二,服务名二,服务名二,服务名二,服务名称二,服务名称三,服务名称四,服务名称四,服务名称四,服务名称四,服务名称四,服务名称四,服务名称四
我不确定,在这一点上,如果只是对CFML或SQL查询中的某些内容进行了一些改动。但是,它确实看起来很有希望。
答案 0 :(得分:5)
如果你真的无法修改表格结构,那么你可以做的最好的就是旧的黑名单之一:
将JOIN
与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
;
使用LIKE
检测节点列表中是否存在特定的serviceID值
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
;
但是,正如您已经注意到,列确实应该正常化。虽然上述方法适用于小型数据集,但它们仍然存在使用“列表”的常见问题。这两种方法都不是非常友好的索引,因此不能很好地扩展。此外,两者都执行字符串比较。因此,最微小的差异可能导致匹配失败。例如,1,4
会匹配两个serviceID,而1,(space)4
或1,4.0
只匹配一个。
根据评论进行更新:
在第二次阅读时,我不确定上述答案是否是您要问的确切问题,但它应该为您的工作提供良好的基础......
如果您不再需要CSV列表,只需使用上述查询之一并照常输出各个查询列。结果将是每行一个服务名称,即:
server1 | Control Name One | Service Name 200
server1 | Control Name One | Service Name 50
..
否则,如果您需要保留逗号分隔值,则可以在查询结果上使用<cfoutput group="..">
。由于结果首先按“主机”排序,类似下面的代码。 NB:要使“群组”正常运作,必须按Host
排序结果,并且必须使用多个cfoutput
代码,如下所示。
<cfoutput query="..." group="Host">
#Host# |
#ControlName# |
<cfoutput>
#ServiceName#,
</cfoutput>
<br>
</cfoutput>
结果应如下所示:
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,
更新2:
我忘了在MySQL中cfoutput group
<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>
{{1}}