sql server 2005+中用for xml path合并字符串的正确写法是:select stuff((select ',' + col from t order by col for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');必须加type和.value()确保安全转义与长度,stuff精准去首分隔符,order by须置于子查询内。

SQL Server 2005+ 中用 FOR XML PATH 合并字符串的写法
老版本 SQL Server(2005–2016)没有 STRING_AGG,FOR XML PATH 是最稳定、兼容性最好的字符串拼接方案。核心思路是把多行值转成 XML 片段,再用 STUFF 去掉开头多余的分隔符。
典型写法:
SELECT STUFF((
SELECT ',' + <code>ProductName</code>
FROM <code>Products</code>
WHERE <code>CategoryID</code> = 1
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS <code>ProductList</code>
注意三点:加 TYPE 确保返回 XML 类型,再用 .value() 安全提取字符串;STUFF 删掉第一个逗号;',' + ProductName 不能写成 ProductName + ','(否则末尾会多一个逗号且难清理)。
为什么必须用 STUFF 而不是 LEFT 或 SUBSTRING
STUFF 更安全——它明确指定从第 1 位开始删 1 个字符,不管结果是否为空或只有一项。而 LEFT(..., LEN(...) - 1) 在空结果时会报错(LEN(NULL) 返回 NULL,LEFT 不接受 NULL 长度),SUBSTRING 同样面临边界判断问题。
- 空集时:
FOR XML PATH('')返回空字符串,STUFF('', 1, 1, '')仍返回空字符串,无异常 - 单行时:
STUFF(',A', 1, 1, '')→'A',逻辑清晰 - 若漏掉
TYPE和.value(),直接用CAST(... AS NVARCHAR)可能触发实体编码(如&变成&)
排序失效?ORDER BY 必须写在子查询里
FOR XML PATH 子查询中的排序不会自动继承外层 ORDER BY,必须显式写在子查询内部,否则拼接顺序不可控(尤其在有索引或执行计划变化时)。
错误写法(排序无效):
SELECT STUFF((SELECT ',' + <code>Name</code> FROM <code>Users</code> FOR XML PATH('')), 1, 1, '')
ORDER BY <code>Name</code> -- 这里排序对拼接顺序没影响
正确写法:
SELECT STUFF((
SELECT ',' + <code>Name</code>
FROM <code>Users</code>
ORDER BY <code>Name</code> -- 必须放在这里
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS <code>Names</code>
另外,ORDER BY 中不能引用外部查询列(即不能 correlated),否则报错 Msg 1033。
性能和字符集陷阱:别忽略 TYPE 和 NVARCHAR(MAX)
不加 TYPE 时,FOR XML PATH('') 返回的是 VARCHAR 或 NVARCHAR 拼接后的隐式转换结果,可能截断超长内容(尤其 > 8000 字节时)。加上 TYPE 强制返回 XML 类型,再用 .value('.', 'NVARCHAR(MAX)') 显式转为最大长度 Unicode 字符串,避免静默截断。
- 如果源字段是
VARCHAR,但拼接后含中文或特殊符号,不用NVARCHAR会乱码 - 在 SQL Server 2005–2012 中,
TEXT或NTEXT类型字段需先CAST成NVARCHAR(MAX)再参与拼接,否则报错 - 大数据量下(>10 万行),
FOR XML PATH性能明显低于 2017+ 的STRING_AGG,但仍是老版本唯一可靠选择
真正容易被忽略的,是 TYPE 和 .value() 的组合——少了任一个,都可能在数据变长、含特殊字符或空集时突然出错。











