array_agg必须配合group by,否则报错;排序需写在函数内;空分组返回null而非空数组,需coalesce兜底;大数据量时易内存溢出。

ARRAY_AGG 必须配合 GROUP BY,否则直接报错
单独写 SELECT ARRAY_AGG(name) FROM users 会触发 PostgreSQL 的聚合上下文检查失败,报错信息通常是 column "name" must appear in the GROUP BY clause or be used in an aggregate function——哪怕你只选了一个字段。这不是语法疏忽,而是语义限制:ARRAY_AGG 是真正的聚合函数,不像 COUNT(*) 那样允许全表隐式分组。
三种合法用法:
- 按业务字段分组:
SELECT dept, ARRAY_AGG(name) FROM employees GROUP BY dept - 多列组合分组:
SELECT dept, role, ARRAY_AGG(id) FROM employees GROUP BY dept, role - 强制全表聚合成单个数组:
SELECT ARRAY_AGG(name) FROM employees GROUP BY ()(注意括号不能省)
ORDER BY 必须写在 ARRAY_AGG() 括号里,否则无效
很多人写成 SELECT dept, ARRAY_AGG(name) FROM t GROUP BY dept ORDER BY name,结果发现数组里元素还是乱序。这是因为外层 ORDER BY 只控制最终结果集的行顺序,不触碰数组内部结构。
真正生效的排序必须嵌在函数调用中:
- 升序(默认):
ARRAY_AGG(name ORDER BY name) - 降序且 NULL 排最后:
ARRAY_AGG(name ORDER BY name DESC NULLS LAST) - 按表达式排序(如长度):
ARRAY_AGG(name ORDER BY LENGTH(name), name)
不加 ORDER BY 时,顺序由执行计划决定,同一查询反复运行可能返回不同排列——前端渲染或校验逻辑容易出问题。
空分组返回 NULL,不是空数组,应用层容易崩
当某组无匹配行(比如 LEFT JOIN 后右表无数据,或 WHERE status = 'active' 但该分组下没人 active),ARRAY_AGG(col) 直接返回 NULL,不是 ARRAY[]::text[]。Python、Django 或 Node.js 解析时极易触发 NoneType 错误或类型不匹配异常。
稳妥兜底写法:
- 显式转空数组:
COALESCE(ARRAY_AGG(col), ARRAY[]::text[])(类型必须匹配,int列就写ARRAY[]::integer[]) - 过滤 NULL + 兜底更干净:
COALESCE(ARRAY_AGG(col) FILTER (WHERE col IS NOT NULL), ARRAY[]::text[]) - 别用
ARRAY_AGG(COALESCE(col, ''))——这只是把单个 NULL 转空字符串,解决不了整组为空的问题
大数据量下内存溢出,work_mem 不是万能解
单个分组聚集几万行时,ARRAY_AGG 可能吃光 work_mem,报错 ERROR: out of memory 或触发磁盘临时文件,拖慢整个查询。
优先考虑业务级优化而非调参:
- 确认是否真需要全量:只需前 N 个?改用子查询限流:
ARRAY(SELECT col FROM (SELECT col FROM t WHERE ... ORDER BY id LIMIT 10) s) - 避免在
SELECT *中滥用:它会让 planner 无法跳过无关列,加重 I/O - 慎用嵌套聚合:
ARRAY_AGG(ROW(id, name))类型推导复杂、序列化开销大,上线前务必压测 - 调大
work_mem是临时手段,设会话级SET work_mem = '64MB'即可,别全局改
最常被忽略的是三件事:排序没写进函数括号、NULL 没过滤导致后续 UNNEST 报错、空组返回 NULL 而非空数组——这三个点漏掉任何一个,都可能让查询在测试环境跑得欢,上线后突然崩。










