【问题标题】:STRING_AGG not behaving as expectedSTRING_AGG 未按预期运行
【发布时间】:2019-03-03 03:53:12
【问题描述】:

我有以下疑问:

WITH cteCountryLanguageMapping AS (
    SELECT * FROM (
        VALUES
            ('Spain', 'English'),
            ('Spain', 'Spanish'),
            ('Sweden', 'English'),
            ('Switzerland', 'English'),
            ('Switzerland', 'French'),
            ('Switzerland', 'German'),
            ('Switzerland', 'Italian')
    ) x ([Country], [Language])
)
SELECT
    [Country],
    CASE COUNT([Language])
        WHEN 1 THEN MAX([Language])
        WHEN 2 THEN STRING_AGG([Language], ' and ')
        ELSE STRING_AGG([Language], ', ')
    END AS [Languages],
    COUNT([Language]) AS [LanguageCount]
FROM cteCountryLanguageMapping
GROUP BY [Country]

我希望瑞士的 Languages 列中的值用逗号分隔,即:

  | Country     | Languages                                 | LanguageCount
--+-------------+-------------------------------------------+--------------
1 | Spain       | Spanish and English                       | 2
2 | Sweden      | English                                   | 1
3 | Switzerland | French, German, Italian, English          | 4

相反,我得到以下输出(4 个值由 and 分隔):

  | Country     | Languages                                 | LanguageCount
--+-------------+-------------------------------------------+--------------
1 | Spain       | Spanish and English                       | 2
2 | Sweden      | English                                   | 1
3 | Switzerland | French and German and Italian and English | 4

我错过了什么?


这是另一个例子:

SELECT y, STRING_AGG(z, '+') AS STRING_AGG_PLUS, STRING_AGG(z, '-') AS STRING_AGG_MINUS
FROM (
    VALUES
        (1, 'a'),
        (1, 'b')
) x (y, z)
GROUP by y

  | y | STRING_AGG_PLUS | STRING_AGG_MINUS
--+---+-----------------+-----------------
1 | 1 | a+b             | a+b

这是 SQL Server 中的错误吗?

【问题讨论】:

  • 似乎是一个错误,无论你如何编写 CASE,它总是返回第一个 STRING_AGG
  • 这是一个优化器错误的美丽之处。更简单、更引人注目的重现:CASE COUNT([Language]) WHEN 1234567 THEN STRING_AGG([Language], ' and ') ELSE STRING_AGG([Language], ', ') END AS [Languages](使用1234567 案例)和CASE COUNT([Language]) WHEN 1234567 THEN STRING_AGG([Language], ' and ') END AS [Languages](省略ELSE——现在匹配失败,表达式变为NULL)。不管“正确”的结果应该是什么,肯定不是这样。
  • 不确定这是否合适,但是....哈哈!由于这不是您缺乏知识,而不是许多人会遇到的真实情况,因此我强烈建议将问题标题更改为比通用的“未按预期工作”更准确,以最大程度地受益于全部。
  • 在生成的执行计划中,第二个STRING_AGG 完全缺失,而是将表达式反弹到第一个,就好像CASE 说过两次STRING_AGG([Language], ' and ')。任何后续的CASEs 也会被吸收。子表达式消除似乎发生了一些非常奇怪的事情。
  • 这个错误似乎专门针对STRING_AGG进行了调整。如果将ELSE 更改为'blargh' + STRING_AGG(...),您将得到'blarghFrench and German...,因此它不正确地将第二个STRING_AGG 与第一个统一起来。最简单的解决方法是将 ELSE 表达式更改为 STRING_AGG([Language] + '', ', ') - 这会破坏 CSE,这表明存在 CSE 忽略 STRING_AGG 的第二个参数的错误。

标签: sql sql-server sql-server-2017 string-aggregation


【解决方案1】:

是的,这是一个错误 (tm),存在于 SQL Server 2017 的所有版本中(截至撰写时)。它已在 Azure SQL Server 和 2019 RC1 中修复。具体来说,优化器中执行公共子表达式消除的部分(确保我们不会计算不必要的表达式)不正确地认为STRING_AGG(x, <separator>) 形式的所有表达式相同,只要x 匹配,无论<separator>是,并将这些与查询中的第一个计算表达式统一起来。

一种解决方法是通过对其执行某种(近)身份转换来确保x 不匹配。由于我们正在处理字符串,连接一个空的就可以了:

SELECT y, STRING_AGG(z, '+') AS STRING_AGG_PLUS, STRING_AGG('' + z, '-') AS STRING_AGG_MINUS
FROM (
    VALUES
        (1, 'a'),
        (1, 'b')
) x (y, z)
GROUP by y

【讨论】:

  • 我允许自己在Azure Feedback上发布这个解决方法
  • dba.stackexchange.com/a/257819/23463 表示它仍然损坏,即使在 CU17 中也是如此。
  • @RossPresser:重新测试过,果然,CU17 并没有真正解决这个问题。修改了答案。
猜你喜欢
  • 2020-10-08
  • 2014-11-12
  • 1970-01-01
  • 2020-06-28
  • 2012-02-18
  • 2018-01-18
  • 2012-06-14
  • 2012-09-21
  • 2014-07-19
相关资源
最近更新 更多