string_agg是专为字符串拼接设计的原生聚合函数,一步完成连接、排序、null过滤,无需中间数组转换;而array_agg生成数组,再转字符串会多出内存分配、遍历和类型协调开销,且默认保留null易引发错误。

ARRAY_AGG 并不比普通拼接“效率更高”——它干的是不同的事,不能直接比效率。 你真正想问的,大概是:为什么在 PostgreSQL 里,用 ARRAY_AGG + array_to_string 拼字符串,不如直接用 STRING_AGG?答案很简单:STRING_AGG 是专为拼字符串设计的,ARRAY_AGG 是为生成数组设计的,硬拿它来拼串,多了一层无谓转换。
STRING_AGG 才是字符串拼接的原生路径
PostgreSQL 的 STRING_AGG 函数从语义、执行计划到底层实现,都围绕“把多行文本连成一个字符串”优化:
- 一步完成连接 + 排序 + NULL 过滤,无需中间数组结构
- 分隔符是必填参数(
delimiter must not be null),强制你明确格式意图 - 执行计划更干净,优化器对它的行为有充分建模,尤其在 PostgreSQL 16 中已支持完整并行聚合
- 空值(
NULL)默认跳过,符合绝大多数业务直觉;而ARRAY_AGG默认保留NULL,后续array_to_string可能输出NULL或报错
ARRAY_AGG 用于拼串时的隐性开销
当你写 array_to_string(ARRAY_AGG(city), ', '),实际发生了三件事:
-
ARRAY_AGG先把所有city值(含NULL)收集进内存,构建一个text[]数组 - 数组构建完毕后,
array_to_string再遍历该数组,逐个转字符串并插入分隔符 - 若字段本身是
TEXT,还涉及一次隐式类型校验;若含大量NULL,数组体积虚高,浪费内存与拷贝带宽
这多出的一次内存分配、一次数组遍历、一次类型协调,在大数据量下会明显拖慢查询。特别是当单组数据超万行时,ARRAY_AGG 构建大数组本身就容易触发 out of memory。
什么时候该用 ARRAY_AGG?
用 ARRAY_AGG 不是为了“更快拼字符串”,而是为了“需要数组语义”:
- 要后续做去重:用
ARRAY_AGG(DISTINCT tag),再配合array_length或下标取值(如(arr)[1]) - 要保留结构供应用层解析:比如返回 JSON API 时,前端更习惯处理数组而非逗号分隔字符串
- 要结合其他数组函数:如
UNNEST、ARRAY_CAT、OVERLAPS等 - 需要显式控制
NULL排序位置(ORDER BY x NULLS LAST),而STRING_AGG不支持该语法
别忽略 PostgreSQL 16 的并行差异
PostgreSQL 16 为 STRING_AGG 和 ARRAY_AGG 都增加了并行聚合能力,但触发条件不同:
-
STRING_AGG的并行要求分隔符不能为NULL(否则退化为串行) -
ARRAY_AGG在并行模式下仍会保留NULL,合并阶段需额外逻辑处理,而STRING_AGG的 partial aggregation 天然跳过NULL - 若查询中混用不支持并行的子句(如某些窗口函数或不可下推的
FILTER),两者都会回退到串行路径
真正影响性能的,从来不是函数名本身,而是你是否让函数干它该干的活——STRING_AGG 拼串,ARRAY_AGG 产数组。混淆二者,问题不在效率,而在语义失控和边界漏判。










