array_agg 本身不比 string_agg 显著慢,真正拖慢的是后续处理;空分隔符有风险;json_agg 比 array_agg 慢1.5–3倍;ORDER BY 在 array_agg 中非必须但几乎总应显式指定。
在大多数场景下,array_agg 本身不比 string_agg 慢多少,真正拖慢的是后续对聚合结果的处理——比如你在应用层还要遍历、反序列化或做 JSON 转换。PostgreSQL 内部对两种函数都做了优化,底层都走相同的聚合框架,但 array_agg 需额外维护元素类型信息和内存结构,尤其当元素是复合类型(如 ROW 或嵌套数组)时,开销会上升明显。
实操建议:
psycopg2 默认返回 list),array_agg 更安全、无歧义string_agg 需手动转义,否则解析易出错;这时表面快,实际埋坑EXPLAIN (ANALYZE, BUFFERS) 对比两者执行计划,重点关注 Aggregate 节点的 Actual Total Time 和 Buffers 消耗,别只看“快几毫秒”有,而且很常见。用 string_agg(col, '') 看似省事,但一旦 col 是 TEXT 且含 NULL,整个结果就变 NULL(因为 string_agg 默认跳过 NULL,但空分隔符下无法区分“全 NULL”和“空字符串拼接”)。更麻烦的是,它彻底丢失原始行边界——你无法还原哪几个值原属同一组。
实操建议:
NULL:改用 string_agg(COALESCE(col::text, 'NULL'), ',')
array_agg + 应用层 ''.join()
string_agg 在 GROUP BY 大量小字符串时内存更省,但一旦单组超 1MB,可能触发临时文件写入,反而比 array_agg 慢
是的,json_agg(或 jsonb_agg)通常比 array_agg 慢 1.5–3 倍,因为它要执行类型推断、引号包裹、转义、编码验证。但注意:这不是“数组 vs 字符串”的问题,而是“裸数组 vs 序列化结构”的问题。如果你本就要返回 JSON 给前端,那提前在数据库里做 jsonb_agg 反而减少应用层序列化压力。
实操建议:
to_json(array_agg(...)) —— 它比 jsonb_agg 多一次转换,还丢失 jsonb 的索引能力int),jsonb_agg 性能接近 array_agg;若混用 text/bool/null,开销陡增pg_stat_statements 查看实际调用中 jsonb_agg 占总执行时间的比例,有些慢其实是上游 JOIN 导致的,不是聚合函数背锅不是必须,但几乎总是应该写。PostgreSQL 不保证 array_agg 的元素顺序,除非显式加上 ORDER BY 子句。很多人测试时发现“没写也有序”,那是巧合——依赖了扫描顺序或 planner 的临时行为,上线后数据量变化或 vacuum 后可能突然乱序。
实操建议:
array_agg(col ORDER BY col) 或 array_agg(col ORDER BY id),不能把 ORDER BY 放在外部ORDER BY col, ctid)避免非确定性ORDER BY 的 array_agg 会触发额外 sort 节点,若已按该字段索引扫描,可利用索引避免排序;否则性能下降明显array_agg 还是 string_agg,而是是否意识到聚合发生在分组之后、是否处理了 NULL、是否依赖未声明的顺序、以及下游是否真的需要那个格式。这些细节比函数名本身更值得盯住。