SQL Server 2019+ 支持 STRING_AGG(DISTINCT expression, separator),DISTINCT 必须紧贴函数名后、首括号内且仅修饰单表达式;低版本需子查询去重;NULL 被跳过,'' 需显式处理;禁止嵌套聚合。

STRING_AGG(DISTINCT ...) 在 SQL Server 2019+ 确实支持,但语法极敏感
不是“不支持”,而是很多人写错位置导致报错 Incorrect syntax near the keyword 'DISTINCT'。SQL Server 2019 开始允许 STRING_AGG(DISTINCT column, ','),但 DISTINCT 必须紧贴函数名后、第一个左括号内,且只能修饰单个表达式。
常见错误写法:
-
STRING_AGG(column, ',') DISTINCT→ 外层加 DISTINCT,语法错误 -
STRING_AGG(DISTINCT(column), ',')→ 多套一层括号,解析失败 -
STRING_AGG(DISTINCT name, category, ',')→ 多列,不支持
正确写法只有一种结构:STRING_AGG(DISTINCT expression, separator),例如:
SELECT STRING_AGG(DISTINCT LOWER(name), '; ') FROM users;
低版本(SQL Server 2017)或复杂去重场景必须用子查询
SQL Server 2017 不识别 DISTINCT 关键字在 STRING_AGG 中,强行使用会直接报错 'DISTINCT' is not recognized。此时唯一可靠方式是把去重逻辑前置。
典型做法是用子查询或 CTE 先 DISTINCT 或 GROUP BY,再聚合:
- 去重 + 排序:先
SELECT DISTINCT dept, name FROM employees,再对结果调用STRING_AGG(name, ', ') WITHIN GROUP (ORDER BY name) - 保留空组(如 LEFT JOIN 后无明细):不能只靠外层 GROUP BY,得把聚合封装进子查询,再和主表关联
- 多列联合去重:用
CONCAT(name, '|', category)合并后再STRING_AGG(DISTINCT ...),否则必须靠GROUP BY name, category+ 子查询
NULL 和空字符串 '' 会让去重失效,必须显式处理
STRING_AGG(DISTINCT ...) 默认跳过 NULL,但不会过滤空字符串 '' —— 它会被当成一个有效值参与去重和拼接,导致结果里出现多余分隔符,比如 'apple,,orange'。
业务上常需统一处理缺失值,推荐以下方式:
- 把
''当NULL:用NULLIF(name, ''),再套STRING_AGG(DISTINCT NULLIF(name, ''), ', ') - 把
NULL当占位符:用COALESCE(name, '(null)'),注意这会使所有NULL变成同一个字符串,影响去重语义 - 过滤掉两者:在子查询中加
WHERE name IS NOT NULL AND name != ''
嵌套聚合或表达式误用会直接报错
STRING_AGG 参数里禁止出现任何其他聚合函数或子查询,例如 STRING_AGG(MAX(name), ', ') 或 STRING_AGG((SELECT TOP 1 x FROM t), ', ') 都会触发错误 Cannot use aggregate function in expression containing aggregate。
想实现“每组取最新 name 再去重拼接”,不能靠嵌套,而要分步:
- 先用
ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY updated_at DESC)标记最新行 - 在子查询中筛选
rn = 1 - 再对结果集执行
STRING_AGG(DISTINCT name, ', ')
真正容易被忽略的是:即使用了 DISTINCT,如果原始数据里有大小写混杂的 'Apple' 和 'apple',它们仍算两个不同值——去重前是否需要标准化(如 LOWER()),得由你明确决定。

















