【问题标题】:Concatenate rows into columns (NO FOR XML PATH('') and recursive CTEs) - SQL Server 2012将行连接成列(NO FOR XML PATH('') 和递归 CTE) - SQL Server 2012
【发布时间】:2020-04-05 09:07:47
【问题描述】:

我手头有一个非常特殊的问题。

简介:我在数据库中有两列需要“分组连接”,在 MySQL 中我只需使用 GROUP_CONCAT 即可获得所需的结果,在 SQL Server 2017 中我将使用 STRING_AGG,我遇到的问题是在 SQL Server 2012 中,它没有这个功能。

现在,在正常情况下,我会使用FOR XML PATH('') 来获取解决方案,这是不可行的,因为我在第三个源应用程序中从编辑器运行查询,我得到的错误是

FOR XML PATH('') 不能在游标内使用

为了论证的缘故,我们假设使用这个函数是完全没有问题的。

我尝试过使用递归 CTE,但是由于执行时间问题,它不可行,UNION ALL 占用太多资源并且无法正常执行(我正在使用数据进行报告)。

由于数据的敏感性,我不会发布数据的屏幕截图,想象一下只有两列,一列有一个 id(多个相同的 id),一列有需要连接的数据(一些字符串)。目标是将第一列中所有相同 id 的第二列连接起来,显然在此过程中使其不同。

示例: 输入:

col1  col2 
1     a
1     b
2     a
3     c

输出:

col1  col2 
1     a/b
2     a
3     c

有人对如何做到这一点有创意吗?

【问题讨论】:

  • 你为什么要简要介绍你的问题,包括你的部分解决方案,这显然不是要走的路,因为你在这里问一个问题?请提供一些(假的?)样本数据和所需的输出。 (在文字中,而不是在图片中!)
  • 我提供介绍是因为这是显而易见的答案,如果我没有提到它,我会得到它作为答案,如果我已经尝试过,没有理由污染帖子无效的答案。
  • 你不能在应用程序中创建一个使用 FOR XML PATH 和从视图中选择的视图吗?
  • 很遗憾无法访问数据库。我所拥有的只是查询数据库的在线“视图”。

标签: sql sql-server tsql string-aggregation


【解决方案1】:

如果您知道需要连接在一起的值的最大数量,则可以使用条件聚合:

select col1,
       stuff( concat(max(case when seqnum = 1 then '/' + col2 end),
                     max(case when seqnum = 2 then '/' + col2 end),
                     max(case when seqnum = 3 then '/' + col2 end),
                     max(case when seqnum = 4 then '/' + col2 end),
                     max(case when seqnum = 5 then '/' + col2 end)
                    ), 1, 1, ''
            ) as col2s                     
from (select t.*, 
             row_number() over (partition by col1 order by col2) as seqnum
      from t
     ) t
group by col1;

您可以使用以下方法获得最大数量:

select top (1) count(*)
from t
group by col1;

【讨论】:

  • 。 . . .你需要聚合。
  • 这是一个很好的解决方法,我已经考虑过了,目前最大数量为 21,但我需要在某个时候动态拉取它。如果我找不到最佳解决方案,可能会对此有所了解。谢谢
  • 好的,因为它处于次优条件下,我认为没有比这更好的解决方案了。谢谢。
【解决方案2】:

您的示例输出似乎错误,因为 'a/b' 的值应该是 2。

尝试以下方法:

declare @t table (col1  int, col2 varchar(100))
insert into @t select 1, 'a'
insert into @t select 2, 'b'
insert into @t select 2, 'a'
insert into @t select 3, 'c'

declare @final_table table (col1  int, col2 varchar(100), col2_all varchar(1000))
insert into @final_table (col1, col2)
select * from @t

declare @col2_all varchar(1000)
declare @Name sysname

update  @final_table 
SET @col2_all = col2_all = COALESCE(CASE COALESCE(@Name, '') 
                                        WHEN col1 THEN @col2_all + '/' + col2
                                        ELSE col2 END, ''), 
                                    @Name = col1;

select col1, col2_grouped = MAX(col2_all)
from @final_table
group by col1

使用 CTE:

;with cte(col1,col2_grouped,rn)
as
(
  select col1, col2 , rn=ROW_NUMBER() over (PARTITION by col1 order by col1)
  from @t
)
,cte2(col1,final_grouped,rn)
as
(
select col1, convert(varchar(max),col2_grouped), 1 from cte where rn=1
union all
select cte2.col1, convert(varchar(max),cte2.final_grouped+'/'+cte.col2_grouped), cte2.rn+1
from cte2
inner join cte on cte.col1 = cte2.col1 and cte.rn=cte2.rn+1
)
select col1, MAX(final_grouped) col2_grouped from cte2 group by col1

请看 dbfiddle here

【讨论】:

  • 是的,太棒了!我在某处看到了合并解决方案,只是不知道如何实现它。有一个问题,我在在线编辑器里不能用insert into,想来想去,这个关键字被禁止了,这个可以用CTE-s写吗?
  • 我确实在网上找到了这个,为给定的解决方案编写了它,但是执行时间太长,我正在考虑更多地在 CTE 中使用您的解决方案(COALESCE)。
  • @BorisJovanovic 动态 sql 怎么样?
  • 函数和程序都出来了。
  • @BorisJovanovic this 工作吗?
猜你喜欢
  • 1970-01-01
  • 2012-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-06
  • 1970-01-01
  • 2012-04-23
  • 2014-05-19
相关资源
最近更新 更多