json_extract返回带引号字符串,需用->>获取裸值;null和路径错误均静默返回null;json_set有隐性风险,推荐json_replace;遍历json数组须while循环;查json字段需建生成列加索引。

JSON_EXTRACT 返回带引号字符串,直接比较会出错
在存储过程中用 JSON_EXTRACT(data, '$.age') 拿到的不是数字 18,而是字符串 "18"。写 IF JSON_EXTRACT(data, '$.age') > 18 THEN 实际触发隐式转换,结果不可靠(常为 NULL 或 0)。
- 必须改用
->>操作符:data->>'$.age'等价于JSON_UNQUOTE(JSON_EXTRACT()),返回裸值,可直接参与数值或字符串比较 - 对
NULL的 JSON 字段调用JSON_EXTRACT静默返回NULL,不报错也不提示,容易漏掉校验 - 路径写错(如
'$.user.name'但实际是'$.profile.name')同样静默返回NULL,逻辑可能被绕过 - 多层嵌套越界(如
'$.items[5]'但数组只有 3 个元素)也返回NULL,不会中断执行
JSON_SET 在存储过程中有隐性风险,优先用 JSON_REPLACEJSON_SET(json_col, '$.status', 'done') 看似简洁,但在存储过程里存在三个实际问题:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
- 字段本身为
NULL时,整个表达式结果仍是NULL,更新失败且无提示 - 路径不存在时会强行新增键,污染原有结构(比如误加
$.tmp_flag) - 并发写入下无锁保护,两个事务同时执行可能导致后写覆盖前写
- 推荐组合:先用
IF JSON_VALID(json_col) THEN ... END IF做前置校验;更新时优先用JSON_REPLACE(只改已有路径,不新增);若需原子递增(如计数器),写成JSON_SET(json_col, '$.count', COALESCE(json_col->>'$.count', 0) + 1),避免先查后改的竞态
存储过程里遍历 JSON 数组只能靠 WHILE 循环
MySQL 存储过程不支持原生 JSON 数组迭代语法,JSON_CONTAINS 和 JSON_SEARCH 只能查,不能逐项处理。唯一可行方案是手动循环:
- 先用
JSON_LENGTH(json_array)获取元素个数(注意:它返回长度,不是最大索引) -
i从0开始,上限设为JSON_LENGTH() - 1(别写成,否则越界取 <code>NULL) - 每次提取用
JSON_EXTRACT(json_array, CONCAT('$[', i, ']')),路径必须动态拼接 - 提取后建议加
JSON_TYPE(item)判断类型,防止把对象当字符串处理引发隐式转换错误
WHERE 条件查 JSON 字段不走索引,必须建生成列
给 JSON 字段加普通 INDEX 完全无效,WHERE JSON_EXTRACT(data, '$.id') = 123 必然全表扫描。想加速查询,必须建生成列再索引:
- 新建表时:定义
id_val INT AS (data->>'$.id') STORED,再CREATE INDEX idx_id ON tbl(id_val) - 已存在表需先
ALTER TABLE ADD COLUMN id_val INT AS (data->>'$.id') STORED,MySQL 5.7.13+ 才支持STORED - 如果字段可能为
NULL或路径不存在,data->>'$.id'返回NULL,生成列也会是NULL,索引仍可用,但查询时要注意IS NOT NULL过滤
复杂点在于 JSON 路径合法性、NULL 传播和生成列维护成本——这些地方没显式报错,但逻辑一跑就偏。










