SQL Server STRING_AGG 函数排序未按预期工作

Posted

技术标签:

【中文标题】SQL Server STRING_AGG 函数排序未按预期工作【英文标题】:SQL Server STRING_AGG function sorting is not working as expected 【发布时间】:2020-10-08 12:37:58 【问题描述】:

我已经打破了这个问题,试图在动态生成的数据集上重现它,但它终于解决了!

这是代码

;with tbl as
(
    select Id,  ClCode, Manager,    ChangeDate
    from (values
    (1, '000005',   'Cierra Vega',      '2017-10-05'),
    (2, '000005',   'Alden Cantrell',   '2017-11-29'),
    (3, '000005',   'Alden Cantrell',   '2017-11-30'),
    (4, '000005',   'Kierra Gentry',    '2018-09-05'),
    (5, '000005',   'Kierra Gentry',    '2018-09-12'),
    (6, '000005',   'Pierre Cox',       '2018-11-06'),
    (7, '000005',   'Thomas Crane',     '2019-09-11'),
    (8, '000005',   'Thomas Crane',     '2019-10-01'),
    (9, '000005',   'Miranda Shaffer',  '2020-04-27'),
    (10,'000360',   'Bradyn Kramer',    '2017-10-06')
    ) as t(Id, ClCode, Manager, ChangeDate)
)
, grouped as                        
(                       
    select c.ClCode
    , count(distinct c.Manager) [ManagerChangesCount]
    , STRING_AGG(c.[Manager], ',') within group (order by c.MinChangeDate) [Managers]
    , STRING_AGG(c.MinChangeDate, ',') within group (order by c.MinChangeDate) [ChangeDates]
    from (                  
        select x.ClCode
        , x.[Manager]
        , min(x.ChangeDate) [MinChangeDate] 
        from tbl x
        group by x.ClCode, x.[Manager]  
    ) c                 
    group by c.ClCode                   
)
select *
from grouped

我的示例数据集包含有关 (ChangeDate) 某些客户 (ClCode) 何时更改其经理 (Manager) 的数据。它是真正的 DWH Clients 维度表(SCD 类型 2)的一部分,因此这些“重复项”仅包含其他列中某处的更改。

我想要实现的目标:我需要一份客户代码列表,其中包含他们的经理被更改的次数,以及这些经理姓名的逗号分隔列表,按更改顺序从左到右排序

ClCode  ManagerChangesCount             Managers                                                                            ChangeDates
000005  6                               Cierra Vega,Alden Cantrell,Kierra Gentry,Pierre Cox,Thomas Crane,Miranda Shaffer    2017-10-05,2017-11-29,2018-09-05,2018-11-06,2019-09-11,2020-04-27
000360  1                               Bradyn Kramer                                                                       2017-10-06

但实际上我收到的结果没有或有一些奇怪的排序

ClCode  ManagerChangesCount Managers                                                                                        ChangeDates
000005  6                               Alden Cantrell,Cierra Vega,Kierra Gentry,Miranda Shaffer,Pierre Cox,Thomas Crane    2017-11-29,2017-10-05,2018-09-05,2020-04-27,2018-11-06,2019-09-11
000360  1                               Bradyn Kramer                                                                       2017-10-06

此查询返回良好的排序:

    如果我删除, count(distinct c.Manager) [ManagerChangesCount] 如果我删除字符串 id=10(第二个客户端) 如果我添加一个过滤条件where ClCode = '000005' 如果我删除数据集中的重复行并从子查询中删除 group by

但对我来说,它看起来像一个错误......我的意思是我的查询中未排序的结果

伙计们,如果你能解决这个问题,请帮助我理解为什么排序不起作用

【问题讨论】:

【参考方案1】:

首先,我同意您遇到的行为不应该发生,但是,Stack Overflow 不是用于报告应用程序的错误。对于 SQL Server,应该在他们的Azure Feedback portal 中完成。

至于解决问题,从您的COUNT 中删除多余的DISTINCT 会导致问题消失。要实现DISTINCT(在SELECT DISTINCTCOUNT(DISTINCT expression) 中),SQL Server 需要首先对结果进行排序,因为它可以轻松删除具有相同排序位置的任何值。因此,您的 STRING_AGG 表达式中表达了排序,即使它们有明确的 ORDER BY 子句。

我之所以说您的DISTINCT 是多余的,是因为在查询中,对于给定的ClCode 值,不会有Manager 的重复值。这是因为您已经在子查询中对 ManagerClCode 进行了分组。如果您单独运行该查询,您会看到 Manager 没有任何重复项:

WITH tbl AS
    (SELECT Id,
            ClCode,
            Manager,
            ChangeDate
     FROM (VALUES (1, '000005', 'Cierra Vega', '2017-10-05'),
                  (2, '000005', 'Alden Cantrell', '2017-11-29'),
                  (3, '000005', 'Alden Cantrell', '2017-11-30'),
                  (4, '000005', 'Kierra Gentry', '2018-09-05'),
                  (5, '000005', 'Kierra Gentry', '2018-09-12'),
                  (6, '000005', 'Pierre Cox', '2018-11-06'),
                  (7, '000005', 'Thomas Crane', '2019-09-11'),
                  (8, '000005', 'Thomas Crane', '2019-10-01'),
                  (9, '000005', 'Miranda Shaffer', '2020-04-27'),
                  (10, '000360', 'Bradyn Kramer', '2017-10-06')) t (Id, ClCode, Manager, ChangeDate) )
SELECT x.ClCode,
       x.[Manager],
       MIN(x.ChangeDate) AS [MinChangeDate]
FROM tbl x
GROUP BY x.ClCode,
         x.[Manager];

因此,COUNT 中的 DISTINCT 只是增加了实例的开销,因为它不是必需的(SQL Server 已经对 GROUP BY 的数据进行了排序,为什么还要让它再次排序呢?) .如果您正在在您已经聚合的查询中使用DISTINCT,那么您很可能不需要它。

【讨论】:

以上是关于SQL Server STRING_AGG 函数排序未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server STRING_AGG() :在 Linq 中等效

2017 年之前 SQL Server 的 String_agg

2017 年之前 SQL Server 的 String_agg

SQL Server - 带有 string_agg 的索引视图

sql server 2016 中的 String_agg

在 SQL Server 中使用 STRING_AGG 获取唯一值