使用CONCAT函数为变量赋值时出现意外聚合的原因咨询
CONCAT赋值变量时意外聚合的原因解析
当使用CONCAT(或CONCAT_WS)将查询结果赋值给仅声明但未初始化的变量时,会出现意外的字符串聚合效果,而非仅保留最后一行的赋值结果,以下是具体分析:
测试代码与执行结果
测试SQL代码
CREATE TABLE #test(val varchar(10)) INSERT INTO #test SELECT * FROM string_split('A|B|C', '|') DECLARE @str1a nvarchar(4000), @str1b nvarchar(4000), @str2 nvarchar(4000), @str3 nvarchar(4000) SELECT @str1a = CONCAT(@str1a, val) FROM #test; PRINT @str1a; SELECT @str1b = CONCAT(val, @str1b) FROM #test; PRINT @str1b; SELECT @str2 = CONCAT(NULL, val) FROM #test; PRINT @str2; SELECT @str3 = CONCAT('', val) FROM #test; PRINT @str3; DROP TABLE #test
执行结果
(影响3行) ABC CBA C C
原因分析
未初始化变量的默认值是NULL
声明但未赋值的SQL变量初始值为NULL,这是核心前提。CONCAT函数对NULL的处理逻辑
CONCAT函数会自动忽略所有NULL参数,只拼接非NULL的内容。当执行SELECT @str1a = CONCAT(@str1a, val)时,SQL Server会逐行遍历表中数据,并依次执行赋值操作:- 第一行:
CONCAT(NULL, 'A')结果为'A',@str1a被赋值为'A' - 第二行:
CONCAT('A', 'B')结果为'AB',@str1a被更新为'AB' - 第三行:
CONCAT('AB', 'C')结果为'ABC',@str1a最终变为'ABC'@str1b = CONCAT(val, @str1b)的逻辑同理,只是拼接顺序相反,最终得到倒序的'CBA'。
- 第一行:
非累加场景的差异
当执行SELECT @str2 = CONCAT(NULL, val)时,每次都是用固定的NULL和当前行的val拼接,结果始终等于当前行的val,但赋值操作是逐行覆盖变量值,最终变量会保留最后一行的结果'C';CONCAT('', val)的逻辑类似,空字符串和val拼接结果还是val,同样逐行覆盖后得到最后一行的'C'。注意事项
这种逐行赋值实现聚合的方式属于SQL Server的非预期行为,官方并不推荐依赖该逻辑实现字符串聚合——因为执行计划的变化可能导致结果不符合预期。若需要正规的字符串聚合,建议使用STRING_AGG函数(SQL Server 2017及以上版本)。
内容的提问来源于stack exchange,提问作者SirParser
相关产品推荐
相关产品推荐

