如何将多列连接成一行?例如:
id name car
1 sam dodge
1 ram maserati
1 john benz
1 NULL mazda
2 kirk lexus
2 Jim rolls
1 GMC
预期的结果集是:
ID name car
1 sam,ram,john dodge,maserati,benz,mazda,GMC
2 kirk,jim lexus,rolls
使用我在 Stack Overflow 上找到的解决方案:
SELECT * FROM (
SELECT t.id,stuff([m].query('/name').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined1],
stuff([m].query('/car').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined2]
FROM dbo.test t
OUTER apply(SELECT (
SELECT id, ','+name AS name
,','+car AS car
FROM test WHERE test.id=t.id
FOR XML PATH('') ,type)
AS M) A)S
GROUP BY id,somefield_combined1,somefield_combined2
有没有更好的解决方案?内部选择来自昂贵的多表连接(不是上面显示的单表“测试”)。该查询位于内联 TVF 中,因此我无法使用临时表。
此外,如果有一个空白列,结果将产生额外的逗号,如
ID name car
1 sam,ram,john,, dodge,maserati,benz,mazda,GMC
2 kirk,jim lexus,rolls
有什么办法可以防止这种情况发生吗?
我使用超过 6 百万行进行了一些测试。在 ID 列上有一个索引。
这是我想出的。
您的初始查询:
这个跑了~23分钟。
我运行了这个版本,这是我第一次学习的版本。在某些方面,它似乎需要更长的时间,但事实并非如此。
这个版本只用了 2 多分钟。
CLR 聚合几乎肯定是最快的方法。但是也许您出于某种原因不想使用一个...
你说这个来源是一个昂贵的查询。
我会
#temp
首先将其具体化为一个表格,以确保它只被评估一次。我为问题中的查询获得的执行计划首先对外部查询中的每一行进行连接,然后删除重复项
id, SomeField_Combined1, SomeField_Combined2
。这是非常浪费的。下面的重写避免了这种情况。
但是对于以下测试数据(对我来说,每个 id 有 1000 个 id,每个 id 有 2156 行)
我仍然发现 Kenneth 的解决方案有两个
XML PATH
调用,速度更快,资源消耗更少。对于其中的每个不同
id
,#test
它执行两个操作而不是一个,但此操作比构造 XML 然后重新解析它要便宜得多。正如 Martin Smith 已经指出的那样,CLR 聚合可能是您最好的选择。同样,将结果存储在临时表中是个好主意。
这是使用 UNPIVOT/PIVOT 的另一种可能的 T-SQL 实现:
它的运行时间与 Kenneth 的解决方案大致相同。
尝试这个
使用
Right
函数删除前导逗号而不是xml
函数使用
case
语句来避免空格的逗号注意:这里
Group by
也可以替换distinct
为,因为我们没有使用任何aggregate
函数