STRING_AGG()仅适用于SQL Server 2017+,需配合GROUP BY使用,自动忽略NULL值,分隔符为常量,排序必须用WITHIN GROUP(ORDER BY...)显式声明,不支持内置去重。

SQL Server 用 STRING_AGG() 最直接
SQL Server 2017+ 原生支持 STRING_AGG(),是合并多行字符串最安全、最高效的方式。它自动处理 NULL 值(默认忽略),还能指定分隔符和排序逻辑。
常见错误是漏写 ORDER BY 子句——不加的话结果顺序不确定,尤其在分组后容易出错:
SELECT STRING_AGG(name, ', ') WITHIN GROUP (ORDER BY id) AS names FROM users WHERE status = 'active';
- 必须用
WITHIN GROUP (ORDER BY ...)显式声明排序依据,否则报错 - 分隔符可以是任意字符串,比如
';'或' | ',但不能是 NULL - 如果所有
name都为 NULL,整个结果返回 NULL,不是空字符串 - 性能上比旧式 FOR XML 方法高 3–5 倍,且可正确走索引
MySQL 用 GROUP_CONCAT() 注意长度限制
MySQL 没有 STRING_AGG(),得靠 GROUP_CONCAT()。它默认只拼接前 1024 字符,超长部分直接截断,这个坑很多人踩过。
典型场景是把用户标签列表拼成一行用于报表导出:
SELECT GROUP_CONCAT(tag ORDER BY created_at SEPARATOR ', ') AS tags FROM user_tags WHERE user_id = 123;
- 必须显式写
SEPARATOR,否则默认用逗号,但空格要自己加 -
GROUP_CONCAT()受系统变量group_concat_max_len限制,默认 1024,超长就无声截断 - 修改长度需执行
SET SESSION group_concat_max_len = 1000000;,仅对当前会话生效 - 无法直接处理 NULL:NULL 值会被转为空字符串,若想跳过,得先用
IFNULL(tag, '')或WHERE tag IS NOT NULL
PostgreSQL 用 STRING_AGG() 但语法稍不同
PostgreSQL 的 STRING_AGG() 功能完整,但参数顺序和 SQL Server 不一样:分隔符是第二个参数,且不强制要求 ORDER BY 子句(不写则无序)。
容易混淆的是误把排序写进函数括号里,导致语法错误:
SELECT STRING_AGG(name, ', ' ORDER BY id) FROM employees;
- 排序必须写在函数内部的
ORDER BY位置,不是WITHIN GROUP - 如果某列含单引号(如 O'Connor),
STRING_AGG()不会自动转义,下游应用需自行处理 - 聚合空值时默认保留,若想排除,得配合
FILTER (WHERE name IS NOT NULL) - 和 SQL Server 不同,PostgreSQL 的
STRING_AGG()支持 DISTINCT,比如STRING_AGG(DISTINCT dept, ', ')
兼容老版本 SQL Server(2016 及更早)只能用 FOR XML
低于 2017 的 SQL Server 没有 STRING_AGG(),只能用 FOR XML + STUFF() 组合。写法绕、易出错,且对特殊字符(如 &、)会自动转义成 <code>&、<。
例如拼接权限名,原始数据含 read&write,结果变成 read&write,这是硬伤:
SELECT STUFF((
SELECT ', ' + permission
FROM user_perms up
WHERE up.user_id = u.id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS perms
FROM users u;
-
TYPE和.value()必须配对使用,否则特殊字符被转义 -
STUFF()第一个参数必须是 NVARCHAR,否则中文可能乱码 - 子查询里不能有 ORDER BY(除非带 TOP),否则报错,排序得靠外层或加
TOP 100 PERCENT(不推荐) - 性能差:每行都触发 XML 构建,大数据量下明显慢于
STRING_AGG()
GROUP_CONCAT() 和 STRING_AGG() 看似一样,参数顺序和默认行为差得挺远。

















