替代 STRING_AGG pre SQL Server 2016

Substitute for STRING_AGG pre SQL Server 2016

我需要通过一组值将 table 与每组的所有匹配行 numbers/id:s 分组。此操作必须在 SQL Server 2016 的范围内完成。

假设我有以下 table(地点):

ID 国家 城市
1 瑞典 斯德哥尔摩
2 挪威 奥斯陆
3 冰岛 雷克雅未克
4 瑞典 斯德哥尔摩

我想要的结果(没有花括号,因为 Stack Overflow 认为它是代码,阻止我发布):

ID Json
1,4 "国家":"瑞典","城市":"斯德哥尔摩"
2 "国家":"挪威","城市":"奥斯陆"
3 "国家":"冰岛","城市":"雷克雅未克"

在 SQL Server 2017 中可以通过以下方式实现上述结果:

SELECT STRING_AGG(ID) ID, (SELECT Country, City FOR JSON PATH) Json
FROM Places GROUP BY Country, City

我使用下面的代码在 SQL Server 2016 中获得了类似的结果。 (但以我实际的数据量和列数,这个方案太慢了。)

SELECT DISTINCT Country, City INTO #temp FROM Places
SELECT (SELECT ID From Places WHERE Country = P.Country AND City = P.City FOR JSON PATH) ID, 
(SELECT Country, City FOR JSON Path) Json FROM #temp P

有没有更有效的方法来实现我想要的结果?

编辑:正如人们建议我尝试“FOR XML 路径”一样,我尝试了下面的代码。这会产生以下错误“Places.ID 在 select 列表中无效,因为它不包含在聚合函数或 GROUP BY 子句中”:

SELECT stuff((select ',' + cast(ID as varchar(max)) for xml path ('')), 1, 1, '') ID, 
(SELECT Country, City FOR JSON PATH) Json
FROM Places GROUP BY Country, City

这是一个您可以尝试使用 for xml path

的解决方案

基本上 select 并将所需的 json 列分组并使用 apply,使用 for xml path 解决方案聚合相关 ID 值;因为外部查询需要引用 apply 的输出,所以它也需要聚合,我选择使用 max

select max(x.Ids), (select country,city for json path) as [Json]
from t
outer apply (
    select Stuff((select ',' + Convert(varchar(10),t2.Id)
    from t t2
    where t2.city=t.city and t2.country=t.country
    for xml path(''),type).value('(./text())[1]','varchar(10)'),1,1,'') as Ids
)x
group by country,city

Working Fiddle

这是另一种可能的解决方案:

Declare @testTable Table (ID int, Country varchar(30), City varchar(30));
 Insert Into @testTable (ID, Country, City)
 Values (1, 'Sweden', 'Stockholm')
      , (2, 'Normway', 'Oslo')
      , (3, 'Iceland', 'Reykjavik')
      , (4, 'Sweden', 'Stockholm');

 Select Distinct
        ID = stuff((Select concat(',', tt2.ID)
                      From @testTable tt2
                     Where tt2.City = tt.City
                       And tt2.Country = tt.Country
                       For xml path (''), Type).value('.', 'varchar(10)'), 1, 1, '')
      , json = (Select Country, City For JSON PATH) 
   From @testTable      tt;

不知道这是否会表现得更好。它本质上是相同的 - 只是使用 DISTINCT 而不是 GROUP BY。