PostgreSQL和Oracle的STRING_AGG/LISTAGG均不支持直接DISTINCT,需先用子查询或CTE去重;MySQL 8.0+的GROUP_CONCAT支持DISTINCT;跨库推荐ARRAY_AGG+ARRAY_TO_STRING或应用层处理。

STRING_AGG 里不能直接写 DISTINCT
PostgreSQL 的 STRING_AGG 函数本身不支持 DISTINCT 关键字,像 STRING_AGG(DISTINCT col, ',') 这种写法会报错:ERROR: syntax error at or near "DISTINCT"。这不是你语法写错,是函数设计如此——它只接受表达式,不解析子句。
正确做法是把去重逻辑提前到聚合前:用子查询或 CTE 先 DISTINCT,再喂给 STRING_AGG。
例如,想按部门合并员工姓名且去重:
SELECT dept,
STRING_AGG(name, ', ' ORDER BY name)
FROM (SELECT DISTINCT dept, name FROM employees) AS deduped
GROUP BY dept;
LISTAGG 在 Oracle 中也不允许直接 DISTINCT
Oracle 的 LISTAGG 同样不接受 DISTINCT 作为参数。尝试 LISTAGG(DISTINCT col, ',') 会触发 ORA-30496: Argument should be a constant 或类似解析错误。
必须用内层去重。注意 Oracle 12c 及以后支持 ORDER BY 子句,但去重仍需前置:
SELECT dept,
LISTAGG(name, ', ') WITHIN GROUP (ORDER BY name)
FROM (SELECT DISTINCT dept, name FROM employees)
GROUP BY dept;
- 如果源数据本身有重复(比如同一人多条记录),漏掉外层
DISTINCT就会导致结果里出现重复名字 -
WITHIN GROUP必须写在LISTAGG后面,不能放在子查询里 - Oracle 11g 不支持
WITHIN GROUP,得用WM_CONCAT(不推荐)或自定义聚合函数
MySQL 8.0+ 用 GROUP_CONCAT + DISTINCT 最省事
MySQL 是少数原生支持 DISTINCT 直接入参的:你可以放心写 GROUP_CONCAT(DISTINCT col ORDER BY col SEPARATOR ', ')。
但要注意几个实际坑点:
-
GROUP_CONCAT默认长度上限是 1024 字符,超长会被截断,需提前设SET SESSION group_concat_max_len = 1000000; -
DISTINCT作用于整个表达式,不是单列——GROUP_CONCAT(DISTINCT a, b)是拼接后去重,不是分别对a和b去重 - 空值(
NULL)默认被忽略,不会出现在结果中;如果要显式显示,得用IFNULL(a, 'N/A')预处理
跨数据库统一写法:用 ARRAY + 转字符串兜底
如果项目要兼容多个数据库(比如测试用 PostgreSQL、上线用 Oracle),硬套各平台语法容易翻车。更稳的方式是绕开字符串聚合函数,先聚合成数组,再转字符串:
PostgreSQL 示例:
SELECT dept,
ARRAY_TO_STRING(ARRAY_AGG(DISTINCT name ORDER BY name), ', ')
FROM employees
GROUP BY dept;
这个组合天然支持 DISTINCT,因为 ARRAY_AGG 允许它,而 ARRAY_TO_STRING 只负责拼接。Oracle 没有原生数组聚合,但可以用 COLLECT + 自定义函数模拟,不过代价高;MySQL 则没有标准数组类型,这条路走不通。
真正需要跨库时,最现实的方案还是在应用层做去重和拼接——SQL 只负责查出明细,别强求一步到位。

















