string_agg去重失效主因是语法错误、空字符串未处理、分组粒度不当、兼容级别不足或缺失索引;正确写法为string_agg(distinct col, ','),distinct须紧贴函数名后,且数据库兼容级需≥160。

STRING_AGG 去重失效,八成不是函数没用对,而是你没写对语法位置或漏了分组逻辑。
STRING_AGG(DISTINCT col, ',') 必须紧贴函数名后,不能套括号或放错位置
很多人想当然写成 STRING_AGG(DISTINCT(col), ',') 或 STRING_AGG(col, ',') DISTINCT,这两种都报错。PostgreSQL 和 SQL Server 2017+ 都要求 DISTINCT 是聚合函数的修饰符,必须直接跟在函数名后、第一个左括号前。
- ✅ 正确:
STRING_AGG(DISTINCT value, ',') - ❌ 错误:
STRING_AGG(DISTINCT(value), ',')(括号非法) - ❌ 错误:
STRING_AGG(value, ',') DISTINCT(位置错,解析失败) - ⚠️ 注意:
DISTINCT作用于整个表达式,比如STRING_AGG(DISTINCT UPPER(name), ';')是对大写化后的值去重
去重前没过滤空字符串,导致结果出现连续分隔符
STRING_AGG 默认跳过 NULL,但不会跳 ''(空字符串)。如果源数据里有 '',它会被当成有效值参与拼接,造成 北京,,上海 这类现象——看起来像“去重失效”,其实是空串被保留了。
- 把空字符串转为
NULL再聚合:STRING_AGG(CASE WHEN col = '' THEN NULL ELSE col END, ',') - 若需保留空串语义但又不想重复,先用子查询清洗:
SELECT STRING_AGG(val, ',') FROM (SELECT DISTINCT NULLIF(col, '') AS val FROM t) t - 别依赖
COALESCE(col, '')后再DISTINCT,那会把所有NULL变成'',反而加剧重复
GROUP BY 维度不对,导致“看似去重,实则分组粒度太粗”
常见场景:想按产品汇总供应商列表并去重,但写了 GROUP BY product 却忘了供应商字段本身有重复组合。这时 STRING_AGG(DISTINCT supplier, ';') 看似生效,但若原始数据中同一 product 下有多条相同 supplier + 不同 date 的记录,DISTINCT 仍只作用于 supplier 字段值,不感知其他列——这不算失效,是预期行为。
- 真正要“按 supplier + date 去重再聚合”,得先在子查询里明确去重维度:
SELECT product, STRING_AGG(supplier, ';') FROM (SELECT DISTINCT product, supplier FROM pu_order WHERE [date] BETWEEN '2025-01-01' AND '2025-01-31') t GROUP BY product - 如果业务要求“每产品只留最新一条采购记录的 supplier”,
DISTINCT就不够用了,得用ROW_NUMBER() OVER (PARTITION BY product, supplier ORDER BY date DESC)预过滤 - 检查执行计划:若发现
STRING_AGG(DISTINCT ...)走的是哈希匹配而非流聚合,说明数据量大且缺少索引,去重可能被截断或性能骤降
兼容级别不足或版本误判,STRING_AGG 根本没启用
哪怕 Management Studio 显示“SQL Server 2022”,若数据库兼容级别仍是 140(对应 2017),DISTINCT 在 STRING_AGG 中会被忽略——不是报错,而是静默退化为普通聚合,看起来就像“去重失效”。
- 立刻验证:
SELECT DATABASEPROPERTYEX(DB_NAME(), 'CompatibilityLevel'),结果必须 ≥160 才支持STRING_AGG(DISTINCT ...) - 升级兼容级:
ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 160(需db_owner权限) - 别信
@@VERSION里的 “16.x” 版本号,那是内部编号;必须含 “2022” 字样才代表真正安装了 2022 实例 - SQL Server 2016 及更早版本不识别
DISTINCT关键字在STRING_AGG中的用法,直接编译失败,根本跑不到运行时
最常被忽略的一点:DISTINCT 和 WITHIN GROUP 的组合会让优化器多做一次排序,如果字段上没索引,大数据量下不仅慢,还可能因内存不足触发溢出到 tempdb,间接导致去重逻辑异常——别只盯着语法,得看执行计划里有没有 “Sort” 算子和 “Spill” 提示。











