JSON_OBJECT键名必须用单引号显式指定,值可为列名或表达式;MySQL 5.7忽略NULL值,8.0默认保留,可用ABSENT ON NULL统一行为;嵌套需子查询或JSON_OBJECTAGG;避免大表无过滤使用以防性能下降。

JSON_OBJECT 生成对象时键名必须显式指定
MySQL 的 JSON_OBJECT 不会自动把列名当键名,你传进去的每个参数都按“键、值、键、值…”成对解析。漏掉某个键名,或者键名写成变量名(比如直接写 name 而不是 'name'),就会报错或生成意外结构。
常见错误现象:ERROR 3142 (HY000): Invalid data type for JSON_OBJECT() key,通常是因为第一个参数不是字符串字面量。
- 键必须用单引号包裹,如
'id'、'email' - 值可以是列名、表达式、NULL 或其他 JSON 函数结果,如
id、CONCAT(first_name, ' ', last_name) - 键名重复时,后出现的会覆盖前面的,不报错但容易遗漏数据
MySQL 5.7 和 8.0 对 NULL 值处理不同
MySQL 5.7 中,如果 JSON_OBJECT 的某个值为 NULL,整个键值对会被直接忽略;而 MySQL 8.0+ 默认保留 NULL 值(即生成 "key": null)。这个差异会影响前端解析逻辑,尤其在做 API 返回时容易出空字段缺失问题。
想统一行为?加 ABSENT ON NULL(8.0.19+)或 NULL ON NULL 显式控制:
SELECT JSON_OBJECT('id', id, 'name', name) ABSENT ON NULL FROM users;
-
ABSENT ON NULL:和 5.7 行为一致,跳过 NULL 键值对 -
NULL ON NULL:强制输出"key": null(默认行为,可省略) - 不加任何修饰符时,5.7 没这选项,8.0 默认是
NULL ON NULL
嵌套对象得靠 JSON_OBJECT 套 JSON_OBJECT 或 JSON_OBJECTAGG
不能直接写 JSON_OBJECT('user', JSON_OBJECT('id', id)) —— 这语法合法,但如果你要从多行聚合出一个用户对象里带数组(比如用户的所有订单),就得配合 JSON_OBJECTAGG 或子查询。
典型场景:查用户 + 其所有地址列表
SELECT JSON_OBJECT(
'id', u.id,
'name', u.name,
'addresses', (
SELECT JSON_ARRAYAGG(
JSON_OBJECT('street', a.street, 'city', a.city)
)
FROM addresses a
WHERE a.user_id = u.id
)
) AS user_json
FROM users u
WHERE u.id = 123;
-
JSON_ARRAYAGG必须搭配GROUP BY或子查询,否则可能只返回一行 - 子查询返回空时,
addresses字段值为NULL(受前述 NULL 策略影响) - 别用
JOIN+GROUP BY直接套JSON_OBJECT,容易因笛卡尔积重复键
性能敏感场景下避免在大表上反复调用 JSON_OBJECT
JSON_OBJECT 是逐行计算函数,没有索引加速。如果在百万级表上 SELECT + JSON_OBJECT,又没加 WHERE 过滤,CPU 和内存压力会明显上升,尤其键多、值含子查询时。
- 先用 WHERE / LIMIT 缩小数据集,再构造 JSON
- 避免在 ORDER BY 或 GROUP BY 外层套 JSON_OBJECT;尽量在聚合完成后再封装
- 如果只是临时调试,用
SELECT ... INTO OUTFILE导出原始数据再用脚本转 JSON 更快
真正麻烦的不是语法,而是嵌套层级深了之后,子查询的 NULL 行为、空集合处理、以及 MySQL 版本间那个静默的 ABSENT 开关——一不留神,前端就收不到字段。


















