一尘不染

STRING_AGG的行为异常

sql

我有以下查询:

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]

我期望瑞士的“语言”列中的值以逗号分隔,即:

  | 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中的错误吗?


阅读 168

收藏
2021-03-17

共1个答案

一尘不染

是的,这是SQL Server 2017的所有版本中存在的错误(tm)(截至撰写时)。它已在Azure SQL Server和2019RC1中修复。具体来说,优化程序中执行通用子表达式消除(确保我们不会计算超出必要数量的表达式)的部分会不正确地认为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
2021-03-17