SQL Server 2017+ 首选 STRING_AGG 函数实现分组字符串拼接,需配合 GROUP BY 使用,支持 WITHIN GROUP(ORDER BY ...) 排序,NULL 默认被跳过,分隔符须为常量。

SQL Server 2017+ 直接用 STRING_AGG 最省事
只要你的 SQL Server 版本 ≥ 2017(含 Azure SQL),STRING_AGG 就是官方推荐、语法简洁、性能可控的首选方案。它专为“分组后拼字符串”设计,不用写自定义函数或嵌套子查询。
常见错误是把 STRING_AGG 当成普通聚合函数直接套在 SELECT 里却忘了加 GROUP BY——它必须配合分组使用,否则报错:Column 'xxx' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause。
-
STRING_AGG第一个参数是表达式(如列名、CONCAT结果),第二个是分隔符(可为','、'; ',甚至空字符串'') - 支持
ORDER BY子句(写在括号内,用WITHIN GROUP (ORDER BY ...)),这点很关键:不显式声明顺序,结果顺序不保证 - 若被拼接字段含
NULL,默认跳过;想保留NULL占位,得提前用ISNULL(col, 'N/A')处理 - 分隔符本身不能是变量或列,只能是常量表达式;动态分隔符需靠外部拼接或动态 SQL
SQL Server 2016 及更早版本只能绕路
没有 STRING_AGG,就得用老办法:FOR XML PATH('') 或自定义聚合函数(CLR)。前者最常用,但容易出错。
典型错误写法是漏掉 TYPE 或没处理特殊字符:比如姓名含 &、 会被转义成 <code>&、<,导致结果变成乱码。
- 安全写法:用
(SELECT col AS [text()] FROM ... FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),其中[text()]和TYPE是关键,避免自动转义 - 分隔符要手动加在每项前(如
',' + col),再用STUFF(..., 1, 1, '')删掉开头多余逗号 - 如果源数据有换行或制表符,
FOR XML默认会保留,可能影响显示;需要额外用REPLACE清洗 - 性能上,大数据量时
FOR XML比STRING_AGG慢且内存开销高,尤其在并行执行计划中易成为瓶颈
STRING_AGG 的 NULL 和空字符串陷阱
很多人以为 STRING_AGG(col, ',') 会把 NULL 当作空字符串拼进去,实际它直接忽略 NULL 值——这在统计标签、权限列表等场景可能造成语义丢失。
- 要显示
NULL为特定占位符(如'(none)'),必须用STRING_AGG(ISNULL(col, '(none)'), ',') - 若列值本身就是空字符串
'',它会被正常拼入,和NULL行为不同,注意区分 - 当整组数据全为
NULL时,STRING_AGG返回NULL;而FOR XML方式返回空字符串'',业务逻辑需兼容这种差异
ORDER BY 在 STRING_AGG 里的写法容易错
不能像普通 ORDER BY 那样放在末尾,必须写在函数括号内,用 WITHIN GROUP 包裹,否则语法报错:Incorrect syntax near the keyword 'ORDER'。
- 正确写法:
STRING_AGG(name, ', ') WITHIN GROUP (ORDER BY sort_order) - 可以多列排序:
WITHIN GROUP (ORDER BY status DESC, created_date ASC) - 排序字段不必出现在
SELECT或GROUP BY中,但必须在源表/子查询里可访问 - 如果排序依据是聚合结果(如
COUNT(*)),需先用子查询或 CTE 提前算好,不能在WITHIN GROUP里直接写聚合函数
STRING_AGG,也别在 2019 环境里还套 FOR XML 模板。顺序、NULL、分隔符这三点,随便漏一个,线上跑出来的结果就可能和预期对不上。

















