JSON_ARRAYAGG是MySQL 8.0+专用聚合函数,将多行某列值聚合成JSON数组,需配合GROUP BY使用,支持NULL跳过、类型保持及与JSON_OBJECT嵌套组合。

MySQL 8.0+ 怎么用 JSON_ARRAYAGG 把多行转成一个 JSON 数组
直接结论:JSON_ARRAYAGG 是 MySQL 8.0 引入的聚合函数,专门干这事——把多行某列(或表达式)的值聚合成一个 JSON 数组。它不是字符串拼接,不依赖 GROUP_CONCAT,天然支持嵌套、NULL 处理和类型保持。
常见错误是把它当 JSON_OBJECT 用,或者在低版本 MySQL(如 5.7)里硬写,结果报错 FUNCTION xxx.JSON_ARRAYAGG does not exist。
- 必须搭配
GROUP BY(哪怕只分一组),否则会把整张表压成一个数组 - 对
NULL值默认跳过(不生成null元素),想保留得先用IFNULL或COALESCE转成占位值 - 如果想让结果里每个元素是对象(比如
{"id":1,"name":"a"}),得配合JSON_OBJECT使用,不能直接传多个字段给JSON_ARRAYAGG
示例:查每个部门的员工姓名列表
SELECT dept_id, JSON_ARRAYAGG(name) AS names FROM employees GROUP BY dept_id;
PostgreSQL 怎么实现类似 JSON_ARRAYAGG 的效果
PostgreSQL 没有同名函数,但用 jsonb_agg() 或 json_agg() 完全等价,且从 9.4 就支持,兼容性更好。
关键区别在于类型:如果字段本身含 NULL 或需要严格类型控制,优先选 jsonb_agg();若需保留原始 JSON 字符串格式(比如带多余空格或顺序),用 json_agg()。
-
jsonb_agg会自动去重、排序 key、忽略重复键,适合后续做@>查询 - 两者都跳过
NULL行,要保留得包一层COALESCE(col, 'null'::jsonb) - 不能直接对
text列调用,否则会当成字符串字面量;想转成字符串数组,得显式用to_jsonb(col)
示例:把用户邮箱聚合成 JSON 数组
SELECT user_id, jsonb_agg(email) AS emails FROM users GROUP BY user_id;
JSON_ARRAYAGG 常见报错和绕不过的坑
最常踩的不是语法错,而是语义错——以为它能“跨表打包”,结果发现只作用于当前查询的输出列,没法直接塞子查询结果或关联后的多列组合。
- 错误写法:
JSON_ARRAYAGG((SELECT name FROM other_table WHERE id = t.ref))→ 报错This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA - 正确做法:先
JOIN或用LATERAL(PostgreSQL)/派生表(MySQL)把数据拉平,再聚合 - 性能隐患:大数据量下,
JSON_ARRAYAGG会在内存中构建整个数组,可能触发max_allowed_packet(MySQL)或work_mem(PG)超限 - 排序很重要:默认不保序,加
ORDER BY子句才可靠,比如JSON_ARRAYAGG(name ORDER BY created_at)
SQL Server 和 SQLite 怎么凑合用
SQL Server 2016+ 用 FOR JSON,本质是格式化输出,不是函数,不能嵌在子查询里直接当值用;SQLite 3.38+ 才有 json_group_array(),旧版只能靠 GROUP_CONCAT + 手动加引号和方括号,极易出错。
- SQL Server 示例(注意必须用子查询包装才能当标量):
SELECT dept_id, (SELECT name FROM employees e2 WHERE e2.dept_id = e1.dept_id FOR JSON PATH) AS names FROM (SELECT DISTINCT dept_id FROM employees) e1;
- SQLite 旧版风险点:用
GROUP_CONCAT('"' || name || '"')无法处理 name 本身含双引号或反斜杠,JSON 不合法 - 所有非原生支持的方案,都绕不开字符转义问题——别信“简单拼接就行”,生产环境一定会遇到特殊字符崩 JSON 解析
真正麻烦的从来不是怎么写出来,而是怎么确保生成的 JSON 在任意数据下都合法、可被下游稳定解析。这点容易被忽略,直到 API 返回 500 或前端 JSON.parse 报错才反应过来。










