【问题标题】:Substitute for STRING_AGG pre SQL Server 2016替代 STRING_AGG pre SQL Server 2016
【发布时间】:2021-09-08 02:58:05
【问题描述】:

我需要按一组值以及每组的所有匹配行号/id:s 对表进行分组。此操作必须在 SQL Server 2016 的范围内完成。

假设我有下表(地点):

ID Country City
1 Sweden Stockholm
2 Norway Oslo
3 Iceland Reykjavik
4 Sweden Stockholm

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

ID Json
1,4 "Country":"Sweden","City":"Stockholm"
2 "Country":"Norway ","City":"Oslo"
3 "Country":"Iceland ","City":"Reykjavik"

在 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 Path”,我尝试了下面的代码。这会产生以下错误“Places.ID 在选择列表中无效,因为它不包含在聚合函数或 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

【问题讨论】:

  • 没有SQL Server 2013。请发出SELECT @@VERSION;并更新问题。
  • 在提出问题时,您需要提供minimal reproducible example:(1) DDL 和样本数据填充,即 CREATE 表加上 INSERT、T-SQL 语句。 (2) 你需要做什么,即逻辑,以及你在 T-SQL 中尝试实现它。 (3) 基于上述#1 中的样本数据的期望输出。 (4) 你的 SQL Server 版本 (SELECT @@version;)
  • 另外,看起来你实际上想要更像 JSON 输出的东西,试试red-gate.com/simple-talk/sql/learn-sql-server/…
  • 那么你所做的实现是不正确的,@Hwende。 FOR XML PATHSTUFF 删除前导分隔符, 2017 年之前的解决方案。
  • 该尝试中没有相关的子查询,@Hwende。这就是为什么。分组时需要使用相关子查询。

标签: sql-server group-by string-aggregation


【解决方案1】:

您可以使用for xml path 尝试以下解决方案

基本上选择和分组所需的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

【讨论】:

  • 感谢您的解决方案。它确实像所说的那样工作。从中学到了一两件事。这可能是在 SQL Server 2017 之前实现我想要的结果的最快方法,对吧?但是,对我来说不幸的是,它的执行速度似乎并不比我帖子中的工作代码示例快。
  • 我相信是的——以这种方式使用 for xml 几乎是首选方法。对于较小的数据集,这是可以接受的;不久前,我从 2016 年到 2019 年进行了一些性能测试,随着行/聚合数量的增加,string_agg() 函数的性能明显提高。不幸的是,XML 性能不佳。
【解决方案2】:

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

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。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-17
    • 2018-08-27
    相关资源
    最近更新 更多