STRING_AGG本身不支持去重,PostgreSQL 15前需用子查询或ARRAY_AGG+ARRAY_TO_STRING实现;GROUP BY时须确保DISTINCT作用于每组内部,避免全局去重错误。

STRING_AGG 本身不支持去重,必须配合 DISTINCT 或子查询
STRING_AGG 是聚合函数,只负责拼接,不处理重复值。直接写 STRING_AGG(DISTINCT col) 会报错:ERROR: syntax error at or near "DISTINCT"(PostgreSQL 15 之前)。从 PostgreSQL 15 开始才支持 DISTINCT 作为 STRING_AGG 的修饰符,但多数生产环境仍是 14 或更早版本,所以得绕行。
最稳妥的做法是先用 DISTINCT 去重,再聚合:
SELECT STRING_AGG(name, ', ') FROM (SELECT DISTINCT name FROM users WHERE dept = 'eng') AS t;
注意子查询必须有别名(如 AS t),否则会报 subquery in FROM must have an alias。
用 ARRAY_AGG + ARRAY_TO_STRING 替代,更灵活控制去重与排序
这个组合能自然兼容 DISTINCT 和 ORDER BY,且语义清晰:
SELECT ARRAY_TO_STRING(ARRAY_AGG(DISTINCT name ORDER BY name), ', ') FROM users WHERE dept = 'eng';
-
ARRAY_AGG支持直接加DISTINCT和ORDER BY,无需子查询 - 结果为空时,
ARRAY_TO_STRING返回NULL(和STRING_AGG行为一致) - 若需空字符串代替
NULL,可套一层COALESCE(..., '')
GROUP BY 场景下误用 DISTINCT 导致聚合粒度错误
常见错误是把 DISTINCT 放在 STRING_AGG 外层,却忘了它作用于整个行集,而非每组内部:
-- ❌ 错误:DISTINCT 作用于全表,破坏 GROUP BY 逻辑 SELECT dept, STRING_AGG(DISTINCT name, '; ') FROM users GROUP BY dept;
这会先全局去重,再按部门分组拼接——结果可能漏掉某些部门里的重复名(其实本该保留跨部门同名,只去本部门内重)。正确写法仍是子查询或 ARRAY_AGG:
-- ✅ 正确:每组内独立去重
SELECT dept,
ARRAY_TO_STRING(ARRAY_AGG(DISTINCT name ORDER BY name), '; ')
FROM users
GROUP BY dept;性能差异:子查询 vs ARRAY_AGG,大数据量时要注意临时空间
两种方式在小数据上无感,但千万级表+高基数去重时:
- 子查询方式会触发两次扫描(一次去重,一次聚合),可能走
HashAggregate+Sort -
ARRAY_AGG(DISTINCT ...)内部用哈希表维护唯一值,内存占用更高;若超出work_mem,会落盘到磁盘,显著拖慢 - 可通过
EXPLAIN (ANALYZE)观察是否出现disk: NMB提示
如果去重后结果集很大(比如上万唯一值),STRING_AGG 拼出的文本可能超长,触发 string_agg overflow(实际是 text 类型长度限制),此时应考虑截断或改用流式处理逻辑。

















