array_agg()需显式ORDER BY保证顺序,FILTER可条件聚合,ARRAY[...]构造多列数组,动态键名应使用jsonb_object_agg而非array_agg。

用 array_agg() 生成基础数组,但默认不保证顺序
PostgreSQL 的 array_agg() 是最直接的聚合数组函数,但它对输入行顺序不做承诺——除非显式排序。如果你依赖数组元素位置(比如第1个是最早创建的记录),漏掉 ORDER BY 就会出错。
常见错误现象:array_agg(user_id) 在不同查询或不同 PostgreSQL 版本中返回顺序不一致,导致应用逻辑偶发失败。
- 正确写法必须带
ORDER BY:array_agg(user_id ORDER BY created_at) - 若需降序,写成
array_agg(user_id ORDER BY created_at DESC) -
NULL值默认被包含在结果数组中;如需过滤,先用WHERE user_id IS NOT NULL或用FILTER (WHERE ...)
用 FILTER 子句做条件聚合,避免子查询嵌套
想只把满足条件的值聚合成数组?别急着写子查询或 CASE WHEN + array_agg 组合——FILTER 更简洁、语义更清晰,且性能通常更好。
使用场景:统计每个部门中「在职」员工 ID 列表,跳过已离职的。
SELECT dept_id,
array_agg(emp_id) FILTER (WHERE status = 'active') AS active_ids
FROM employees
GROUP BY dept_id;
注意点:
-
FILTER必须紧跟在聚合函数后,括号内只能是布尔表达式 - 不能和窗口函数混用(
array_agg() OVER (...) FILTER (...)是非法语法) - 如果所有行都不满足
FILTER条件,结果为NULL而非空数组{};需要空数组可用COALESCE(..., ARRAY[]::int[])
拼接多列或表达式进一个数组,用 ARRAY[...] 构造器配合 array_agg
有时要聚合的不是单列,而是组合字段(比如 [user_id, username] 这样的二维结构)。这时不能直接 array_agg(user_id, username)——语法报错。
正确做法是先用 ARRAY[...] 构造每行的子数组,再用 array_agg 外层聚合:
SELECT array_agg(ARRAY[user_id, username] ORDER BY user_id) FROM users WHERE active;
输出类似:{{1,"alice"},{2,"bob"}}(即 text[][] 类型)。
- 类型需一致:若
user_id是int,username是text,整个ARRAY[...]会自动转为text[];如需保持强类型,显式转换,例如ARRAY[user_id::text, username] - 嵌套太深会影响可读性和 JSON 序列化兼容性;真要结构化数据,考虑用
jsonb_agg(jsonb_build_object(...))更合适
动态键名数组?PostgreSQL 没有原生支持,得绕道 jsonb_object_agg
如果所谓“动态数组”实际是指「键名不固定、由字段值决定」(比如把 category 当 key,count 当 value 拼成 {"books": 12, "music": 8}),那 array_agg 不适用——它只产出有序列表,不处理映射关系。
此时应换用 jsonb_object_agg(key, value):
SELECT jsonb_object_agg(category, total) FROM ( SELECT category, COUNT(*) AS total FROM products GROUP BY category ) t;
关键限制:
-
key表达式结果必须是text;非 text 类型需强制转,例如category::text - 重复 key 会导致最后一条覆盖前面的(无警告);如需合并,得先
GROUP BY key再聚合 value - 不支持直接生成
ARRAYofJSON对象;若要 [{k:"a",v:1},{k:"b",v:2}],改用jsonb_agg(jsonb_build_object('k', k, 'v', v))
真正难的不是语法,是分清你要的是「有序值集合」还是「键值映射」——选错函数,后面所有处理都得推倒重来。











