Sql 字符串_AGG的行为与预期不符
我有以下疑问:Sql 字符串_AGG的行为与预期不符,sql,sql-server,sql-server-2017,string-aggregation,Sql,Sql Server,Sql Server 2017,String Aggregation,我有以下疑问: WITH cteCountryLanguageMapping AS ( SELECT * FROM ( VALUES ('Spain', 'English'), ('Spain', 'Spanish'), ('Sweden', 'English'), ('Switzerland', 'English'), ('Switzerland',
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个值由和分隔):
我错过了什么
下面是另一个例子:
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中的一个bug吗?是的,这是一个bug(tm),存在于SQL Server 2017的所有版本中(截至撰写时)。它已在Azure SQL Server和2019 RC1中修复。具体地说,优化器中执行公共子表达式消除的部分(确保我们计算表达式的次数不超过必要的次数)不正确地认为只要x
匹配,所有字符串\u AGG(x,)
形式的表达式都是相同的,无论
是什么,并将它们与查询中的第一个计算表达式相统一
一种解决方法是通过对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
这似乎是一个bug,它总是返回第一个字符串\u AGG,无论您如何编写案例。这是一个优化程序bug的优点。更简单、更引人注目的复制:CASE COUNT([Language])当1234567然后是STRING_AGG([Language],'and'),否则STRING_AGG([Language],',')结束为[Languages]
(使用1234567
CASE)和CASE COUNT([Language]),当1234567然后是STRING_AGG([Language],'and')结束为[Languages
(省略ELSE
——现在匹配失败,表达式变为NULL
)。无论“正确”是什么结果应该是,当然不是。不确定这是否合适,但是……哈哈!因为这不是你缺乏知识,而不是许多人会遇到的真实情况,我强烈建议将问题标题更改为比一般的“未按预期工作”更准确,以使所有人的利益最大化。在生成的执行计划中,第二个字符串_AGG
完全丢失,相反,表达式反弹到第一个,就好像案例
说了字符串_AGG([语言],“和”)
两次。任何后续的案例都会被吸收。看起来子表达式消除过程中发生了一些非常奇怪的事情。此错误似乎被特别调整为字符串\u AGG
。如果ELSE
更改为'blargh'+STRING\u AGG(…)
,您将得到'blarghFrench and German…
,因此它不正确地将第二个STRING_AGG
与第一个统一起来。最简单的解决方法是将ELSE
表达式更改为STRING_AGG([语言]+'','))
--这击败了CSE,表明CSE忽略了STRING\u AGG
的第二个参数,这是一个错误。我允许自己发布此解决方法,指出它仍然损坏,即使在CU17中也是如此。@RossPresser:重新测试了它,果然,CU17实际上并没有修复此问题。修改了答案。
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