我们可以在使用GROUP BY ROLLUP ()的查询中使用SQL Server STRING_AGG()吗

问题描述 投票:0回答:2

TL;DR:我们可以在使用 GROUP BY ROLLUP(...) 的查询中使用 STRING_AGG 聚合函数吗?


我正在转换使用 STUFF( (SELECT...FOR XML) ) 方法的旧代码,以便它使用 STRING_AGG 代替。

使用 ROLLUP 时我似乎无法使用 STRING_AGG,因为我收到错误

与 CUBE、ROLLUP 或 GROUPING SET 查询一起使用的聚合函数必须提供子聚合的合并。要解决此问题,请删除聚合函数或在 GROUP BY 子句上使用 UNION ALL 编写查询。

有效的代码,只需使用 GROUP BY ....

DROP TABLE IF EXISTS RollupTest
CREATE TABLE RollupTest (UserId INT, RoleName VARCHAR(20));
INSERT RollupTest VALUES (1, 'Boss'), (1, 'Dogsbody'), (2, 'Dogsbody'), (2, 'another'), (2, 'parent'), (3, 'another')

SELECT UserId, NumRoles=COUNT(*), RoleNames=STRING_AGG(RoleName, ', ')
  FROM RollupTest
 GROUP BY UserId
/* output is...
UserId      NumRoles    RoleNames
----------- ----------- -------------------------
1           2           Boss, Dogsbody
2           3           Dogsbody, another, parent
3           1           another
*/

更改为使用 ROLLUP 获取总计行,期望为总计行中的聚合名称列获取 NULL...

DROP TABLE IF EXISTS RollupTest
CREATE TABLE RollupTest (UserId INT, RoleName VARCHAR(20));
INSERT RollupTest VALUES (1, 'Boss'), (1, 'Dogsbody'), (2, 'Dogsbody'), (2, 'another'), (2, 'parent'), (3, 'another')

SELECT UserId, NumRoles=COUNT(*), RoleNames=STRING_AGG(RoleName, ', ')
  FROM RollupTest
 GROUP BY ROLLUP(UserId)        --<<< just added ROLLUP here

/* expected output to be something like the following...
UserId      NumRoles    RoleNames
----------- ----------- -------------------------
1           2           Boss, Dogsbody
2           3           Dogsbody, another, parent
3           1           another
NULL        6           NULL

*/

...我们得到了错误。 我尝试使用 GROUPING(...) 来尝试避免在总计行上调用 STRING_AGG() ,但这没有什么区别。

我想这很明显,也许 STRING_AGG() 出于合理的原因无法被汇总,但我看不到它。我不认为错误消息有帮助,但我可能有点密集。 当然,使用 FOR XML 的旧方法可以与 ROLLUP 一起使用。

sql-server aggregate-functions
2个回答
1
投票

正如您所见,在

STRING_AGG
中使用任何类型的分组集时,不允许使用
GROUP BY

您可以按如下方式破解它

SELECT
  UserId,
  NumRoles = SUM(NumUsers),
  RoleNames = STRING_AGG(RoleName, ', ')
FROM (
    SELECT
      UserId,
      RoleName,
      NumUsers = COUNT(*),
      IsGrouped = GROUPING(UserId)
    FROM RollupTest rt
    GROUP BY GROUPING SETS (
        (UserId, RoleName),
        (RoleName)
    )
) rt
GROUP BY
  IsGrouped,
  UserId
ORDER BY
  IsGrouped,
  UserId;

db<>小提琴

这里的想法如下:

  • 在派生子查询中使用
    GROUPING SETS
    将汇总行分解为单独的
    UserId, NULL
    行。计算该级别的总计数。您仍然可以获得所有原始行。
  • 在外侧按
    UserId
    IsGrouped
  • 分组
  • 返回之前计算的计数的总和,以及聚合的字符串。
  • 您可以使用
    IsGrouped
    列进行排序和条件判断,但这并不是绝对必要的。

请注意,此解决方案中没有使用自连接或窗口函数,尽管它确实需要第二次排序。


0
投票

将数据插入临时和分组依据

SELECT value AS UserId
    INTO #a
    FROM STRING_SPLIT(@ObjectIds, ',')
    GROUP BY value;

然后进行聚合

SELECT @ObjectIds = STRING_AGG(UserId, ',')
    FROM #a

祝你好运

© www.soinside.com 2019 - 2024. All rights reserved.