JSON_ARRAYAGG 默认保留NULL值而非忽略;需用WHERE过滤或COALESCE+JSON_ARRAY()处理空值,ORDER BY须内置,构造对象数组须嵌套JSON_OBJECT。

JSON_ARRAYAGG 会把空值变成 null 还是直接忽略?
默认情况下,JSON_ARRAYAGG 会把 NULL 值原样塞进数组里,不是跳过,也不是报错。比如字段 name 有几行是 NULL,结果数组里就会出现 null 元素——这常导致前端解析失败或逻辑误判。
解决办法是显式过滤:用 WHERE name IS NOT NULL,或者更稳妥地用 IFNULL(name, '') 配合 JSON_OBJECT 构造键值对,避免裸 NULL 进入聚合。
- 别依赖默认行为,空值处理必须主动控制
- 如果源字段允许为空,且业务上“空”和“缺失”语义不同,建议先用
COALESCE统一转成占位符(如'(unset)')再聚合 - 注意:在
GROUP BY场景下,WHERE过滤发生在分组前,而HAVING是分组后,空值过滤务必放WHERE子句
怎么让 JSON_ARRAYAGG 输出带 key 的对象数组,而不是纯值数组?
JSON_ARRAYAGG 本身只负责“聚合”,真正决定数组元素结构的是它括号里的表达式。想得到 [{"id":1,"name":"a"},{"id":2,"name":"b"}],就得在里面套 JSON_OBJECT,不能直接写字段列表。
常见错误是写成 JSON_ARRAYAGG(id, name) —— 这会报语法错误,因为 JSON_ARRAYAGG 只接受单个表达式参数。
- 正确写法:
JSON_ARRAYAGG(JSON_OBJECT('id', id, 'name', name)) - 字段名必须是字符串字面量(带引号),不能是变量或列别名
- 如果某个字段含特殊字符(比如带空格或连字符),要用
JSON_OBJECT的键名加引号,MySQL 8.0.17+ 支持双引号或单引号,但统一用单引号更稳 - 性能提示:嵌套
JSON_OBJECT比纯字段聚合稍慢,大数据量时建议先在应用层组装,数据库层只做简单聚合
ORDER BY 在 JSON_ARRAYAGG 里怎么生效?
JSON_ARRAYAGG 支持内置 ORDER BY 子句,格式是 JSON_ARRAYAGG(expr ORDER BY col1, col2)。这个排序发生在聚合过程中,不是外部 ORDER BY——后者只影响最终结果集顺序,不影响数组内部元素顺序。
容易踩的坑是混淆两者:比如在外层加 ORDER BY created_at,以为能控制数组里对象的排列,其实没用。
- 必须把排序逻辑写进
JSON_ARRAYAGG括号内,例如:JSON_ARRAYAGG(JSON_OBJECT('x', x) ORDER BY x DESC) - 支持多列、ASC/DESC、表达式(如
LENGTH(name)),但不能引用聚合别名 - MySQL 5.7 不支持该语法,必须升级到 8.0+;若无法升级,只能靠子查询 +
GROUP_CONCAT拼接后手动解析(不推荐)
为什么 GROUP BY 后 JSON_ARRAYAGG 返回 NULL?
最常见原因是分组后某组没有任何匹配行——比如 LEFT JOIN 的右表无记录,又没加 COALESCE 处理,JSON_ARRAYAGG 就返回 NULL 而非空数组 []。
这跟多数人直觉相反:空集合聚合结果不是 [],而是 NULL。
- 修复方式:用
COALESCE(JSON_ARRAYAGG(...), JSON_ARRAY())显式转为空数组 -
JSON_ARRAY()是 MySQL 5.7+ 提供的空数组构造函数,比写'[]'字符串安全(避免类型不匹配) - 特别注意
LEFT JOIN场景:即使左表有数据,右表无匹配时,JSON_ARRAYAGG依然返回NULL,必须包裹COALESCE
实际写的时候,JSON_ARRAYAGG 看似简单,但空值、排序作用域、分组边界、版本兼容这四点最容易出问题。尤其线上环境,建议所有使用都加上 COALESCE(..., JSON_ARRAY()) 和显式 WHERE 过滤,别信“应该没问题”。


















