ARRAY_AGG优于STRING_AGG,因其返回原生数组支持下标取值、去重排序、NULL控制及JSONB互转;STRING_AGG生成字符串后需额外解析,灵活性差且易出错。

因为 ARRAY_AGG 返回的是 SQL 数组类型,不是字符串——这意味着你能继续在数据库里做数组操作,而 STRING_AGG 一旦拼完就“定型”成字符串,后续想取元素、判长度、去重保序都得靠正则或二次解析,既慢又脆。
ARRAY_AGG 支持原生数组运算,STRING_AGG 拼完就封印了
拼接结果是数组,就能直接用 PostgreSQL 提供的数组函数:下标取值、长度统计、元素包含判断、甚至和 JSONB 互转。比如要取每个用户第一个标签:(ARRAY_AGG(tag ORDER BY created_at DESC))[1];要统计去重后标签数:array_length(ARRAY_AGG(DISTINCT tag), 1)。而 STRING_AGG 输出的是 TEXT,你没法用 [1] 取第一个,也没法直接 array_length 算个数,只能切分再转数组,多一层转换就多一次开销和出错可能。
去重 + 排序 + NULL 控制更可控
ARRAY_AGG(DISTINCT tag ORDER BY tag NULLS LAST) 一行搞定去重、升序、NULL 排最后。而 STRING_AGG(DISTINCT tag, ',') 虽然也能去重拼接,但:
- 它不支持 NULLS LAST,NULL 总是排最前,业务上常不合理
- 如果字段含空格(如 'VIP ' 和 'VIP'),DISTINCT 不会自动 trim,必须显式写 ARRAY_AGG(DISTINCT trim(tag))
- 若需按优先级排序(比如把 'VIP' 排最前),STRING_AGG 的 ORDER BY 只能作用于原始值,无法加权重表达式,ARRAY_AGG 却可以:ARRAY_AGG(tag ORDER BY (tag = 'VIP') DESC, tag)
并行聚合下行为更确定,尤其含 NULL 或大分隔符时
PostgreSQL 16+ 对 ARRAY_AGG 和 STRING_AGG 都加了并行支持,但实际表现有差异:
- STRING_AGG 的 delimiter 参数不能为 NULL,否则整个聚合退化为串行,容易被忽略
- ARRAY_AGG 默认保留 NULL,配合 FILTER (WHERE col IS NOT NULL) 可精确控制,而 STRING_AGG 默认跳过 NULL,看似省事,但在并行分片合并时,不同 worker 对 NULL 的处理边界可能影响最终顺序
- 当你要拼接的字段本身是 JSONB 或需要后续转 JSONB(比如 API 返回),ARRAY_AGG 直接输出 JSONB[],STRING_AGG 得先拼再 jsonb_agg 或 jsonb_build_array,绕路且易格式错乱
真正关键的不是“哪个更快”,而是“拼完之后你还想干什么”。如果只是生成报表字段、导出 CSV,STRING_AGG 确实够用;但只要涉及任何后续计算、过滤、嵌套结构或与应用层数据结构对齐(比如匹配 Go 的 []string 或 Python 的 list),ARRAY_AGG 就不是“更实用”,而是“不可替代”。

















