SQL JSON_ARRAY、JSON_OBJECT 构建技巧

1次阅读

json_array混用NULL与空字符串易致类型混淆,json_object键名不可为列名,嵌套需防多行与层级过深,版本差异及性能问题须提前规避。

SQL JSON_ARRAY、JSON_OBJECT 构建技巧

JSON_ARRAY 里混用 NULL 和空字符串容易踩坑

mysqlJSON_ARRAY 会把 SQL NULL 直接转成 JSON null,但空字符串 '' 会保留为 "" —— 看似一样,实际类型不同,前端解析或后续 JSON 函数(比如 JSON_CONTAINS)可能行为不一致。

常见错误现象:JSON_ARRAY(col1, col2) 返回 [null, ""],结果被当成两个有效值处理,导致统计或条件判断出错。

  • 明确区分意图:真缺失用 NULL,空内容用 '';别依赖默认转换
  • 需要统一为空值时,提前用 NULLIF(col, '') 过滤掉空字符串
  • 如果字段可能为 NULL 且你不想它出现在数组里,得用 CASE WHEN col IS NOT NULL THEN col END 做条件包裹,再进 JSON_ARRAY

JSON_OBJECT 键名必须是字符串字面量或表达式,不能是列别名

JSON_OBJECT(key_col, value_col) 是错的 —— key_col 是列名,不是字符串,MySQL 会报错 Incorrect parameters in the call to native function 'JSON_OBJECT'

使用场景:动态构造键值对,比如把表中两列转成对象,但键名本身来自数据(如配置表的 setting_keysetting_value)。

  • 正确做法是用 JSON_OBJECTAGG 配合 GROUP BY,它专为“列转键值”设计
  • 如果非要用 JSON_OBJECT,键必须显式写死或用字符串表达式,例如 JSON_OBJECT('name', name, 'id', id)
  • 想从列值生成键?只能拼接 + JSON_SET 或用应用层处理,SQL 层做不到运行时动态键名

嵌套 JSON_ARRAY 和 JSON_OBJECT 时要注意括号层级和类型校验

嵌套太深或类型不匹配会导致 Invalid JSON text 错误,尤其在子查询返回多行时,JSON_ARRAY(select ...) 会直接失败 —— 它只接受标量或单值表达式,不支持多行结果集。

性能影响:每层 JSON 构造都触发一次序列化,嵌套超过 3–4 层时,CPU 开销明显上升,特别是配合大字段或 GROUP_CONCAT 使用时。

  • 多行聚合用 JSON_ARRAYAGG,不是 JSON_ARRAY;后者只拼几个已知值
  • 嵌套前先确认子表达式是否返回单值:比如 (SELECT MAX(price) FROM items WHERE order_id = o.id) 可以,(SELECT price FROM items WHERE order_id = o.id) 不行
  • 调试技巧:先把子表达式单独执行,看结果是不是合法 JSON 字符串;不是就加 CAST(... AS JSON) 强制转换

MySQL 5.7 和 8.0 在 JSON 函数行为上存在隐性差异

5.7 的 JSON_OBJECT 不支持重复键去重,8.0 默认保留最后一个;5.7 对非法 UTF-8 字符更宽容,8.0 直接报错 Invalid utf8mb4 character

兼容性影响:同一段 SQL 在两个版本可能返回不同结构,甚至执行失败,线上迁移时容易漏测。

  • 建表时对 JSON 字段加 GENERATED column 并设 STORED,比运行时拼接更稳定
  • 避免在 WHERE 子句里用 JSON_EXTRACT(JSON_OBJECT(...), '$.x') 做条件,索引无法生效,5.7 尤其慢
  • 跨版本部署前,用 SELECT VERSION() 检查,并在测试库跑 EXPLAIN format=JSON 看 JSON 函数是否被下推

最麻烦的其实是混合了业务逻辑和 JSON 结构的查询 —— 比如一边要按时间排序,一边还要把关联的标签数组塞进 JSON_OBJECT 里。这时候函数嵌套、NULL 处理、版本兼容全撞在一起,单靠 SQL 很难干净解决。

text=ZqhQzanResources