19

谁能帮我使这个查询适用于 SQL Server 2014?

这适用于 Postgresql,可能适用于 SQL Server 2017。在 Oracle 上,它listagg不是string_agg.

这是SQL:

select 
    string_agg(t.id,',') AS id
from 
    Table t

我在网站上检查了应该使用一些 xml 选项,但我无法理解。

4

2 回答 2

32

在 SQL Server pre-2017 中,您可以执行以下操作:

select stuff( (select ',' + cast(t.id as varchar(max))
               from tabel t
               for xml path ('')
              ), 1, 1, ''
            );

的唯一目的stuff()是删除初始逗号。这项工作由for xml path.

于 2018-03-19T10:49:36.247 回答
9

请注意,对于某些字符,使用 时会转义值FOR XML PATH,例如:

SELECT STUFF((SELECT ',' + V.String
              FROM (VALUES('7 > 5'),('Salt & pepper'),('2
lines'))V(String)
              FOR XML PATH('')),1,1,'');

这将返回以下字符串:

7 > 5,Salt & pepper,2
lines'

这不太可能发生。您可以使用TYPE然后获取 XML 的值来解决此问题:

SELECT STUFF((SELECT ',' + V.String
              FROM (VALUES('7 > 5'),('Salt & pepper'),('2
lines'))V(String)
              FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'');

这将返回以下字符串:

7 > 5,Salt & pepper,2
lines

这将复制以下行为:

SELECT STRING_AGG(V.String,',')
FROM VALUES('7 > 5'),('Salt & pepper'),('2
lines'))V(String);

当然,有时您可能想要对数据进行分组,上面没有说明。为此,您需要使用相关子查询。取以下样本数据:

CREATE TABLE dbo.MyTable (ID int IDENTITY(1,1),
                          GroupID int,
                          SomeCharacter char(1));

INSERT INTO dbo.MyTable (GroupID, SomeCharacter)
VALUES (1,'A'), (1,'B'), (1,'D'),
       (2,'C'), (2,NULL), (2,'Z');

由此想要得到以下结果:

组ID 人物
1 A,B,D
2 C,Z

为此,您需要执行以下操作:

SELECT MT.GroupID,
       STUFF((SELECT ',' + sq.SomeCharacter 
              FROM dbo.MyTable sq
              WHERE sq.GroupID = MT.GroupID --This is your correlated join and should be on the same columns as your GROUP BY
                                            --You "JOIN" on the columns that would have been in the PARTITION BY
              FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'')
FROM dbo.MyTable MT
GROUP BY MT.GroupID; --I use GROUP BY rather than DISTINCT as we are technically aggregating here

因此,如果您在 2 列上进行分组,那么您的子查询将有 2 个子句WHERE: WHERE MT.SomeColumn = sq.SomeColumn AND MT.AnotherColumn = sq.AnotherColumn,而您的外部GROUP BY将是GROUP BY MT.SomeColumn, MT.AnotherColumn.


最后,让我们在其中添加一个ORDER BY,您也在子查询中定义它。例如,假设您想ID按字符串聚合中的降序值对数据进行排序:

SELECT MT.GroupID,
       STUFF((SELECT ',' + sq.SomeCharacter 
              FROM dbo.MyTable sq
              WHERE sq.GroupID = MT.GroupID
              ORDER BY sq.ID DESC --This is identical to the ORDER BY you would have in your OVER clause
              FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'')
FROM dbo.MyTable MT
GROUP BY MT.GroupID;

For 会产生以下结果:

组ID 人物
1 D,B,A
2 Z,C

STRING_AGG不出所料,由于多次引用表(如果需要执行多个聚合,则需要多个子查询),这永远不会像 a 那样高效,但是索引良好的表将极大地帮助 RDBMS。如果性能确实是一个问题,因为您在单个查询中执行多个字符串聚合,那么我建议您需要重新考虑是否需要聚合,或者是时候考虑升级了。

于 2020-10-06T14:05:44.113 回答