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

JSON_EXTRACT 返回带引号字符串,直接比较会出错
在存储过程中用 JSON_EXTRACT(data, '$.age') 拿到的不是数字 18,而是带双引号的 JSON 字符串 "18"。所以写 IF JSON_EXTRACT(data, '$.age') > 18 THEN ... 实际上是在做字符串和整数比较,MySQL 会尝试隐式转换,但结果不可靠(常为 NULL 或意外值)。
正确做法是用 ->> 操作符替代:data->>'$.age',它等价于 JSON_UNQUOTE(JSON_EXTRACT(...)),返回裸值,可直接参与数值或字符串比较。
其他常见错误场景:
- 对 NULL 的 JSON 字段调用
JSON_EXTRACT总是返回NULL,不报错也不提示,容易漏掉校验 - 路径写错(如
'$.user.name'但实际是'$.profile.name')也静默返回NULL,逻辑可能被绕过 - 多层嵌套越界(如
'$.items[5]'但数组只有 3 个元素)同样返回NULL,不会中断执行
在存储过程中安全更新 JSON 字段,别直接用 JSON_SET
JSON_SET 看似方便,但在存储过程里有三个隐性风险:字段本身为 NULL 时结果仍是 NULL;路径不存在时会强行新增键,污染结构;并发写入下无锁保护,可能覆盖他人修改。
推荐组合策略:
- 更新前先用
JSON_VALID(json_col)判断有效性,避免后续操作全失效 - 优先用
JSON_REPLACE替代JSON_SET,它只改已有路径,不新增字段 - 若需原子递增(如计数器),用
JSON_SET(json_col, '$.count', COALESCE(json_col->>'$.count', 0) + 1),避免先查后改的竞态 - 对数组追加元素,用
JSON_ARRAY_APPEND(json_col, '$.tags', 'new_tag'),比拼接字符串更可靠
在存储过程中遍历 JSON 数组只能靠 WHILE + 动态路径
MySQL 存储过程不支持原生 JSON 数组迭代语法,JSON_CONTAINS 和 JSON_SEARCH 只能查,不能逐项处理。唯一可行方案是手动循环。
关键步骤:
- 先用
JSON_LENGTH(json_array)获取长度,注意它返回的是元素个数,不是最大索引 - 用
WHILE i ,i 从 <code>0开始,上限是JSON_LENGTH() - 1(别写成导致越界取 <code>NULL) - 每次提取用
JSON_EXTRACT(json_array, CONCAT('$[', i, ']')),路径必须动态拼接 - 提取后建议加
JSON_TYPE(item)判断类型,防止把对象当字符串处理引发隐式转换错误
WHERE 条件里查 JSON 字段不走索引,必须建生成列
写 WHERE JSON_EXTRACT(data, '$.status') = 'active' 或 WHERE data->>'$.status' = 'active',MySQL 一定会全表扫描——JSON 字段本身无法直接建索引。
想加速查询,必须提前建生成列并索引:
- 建表时定义:
status_val VARCHAR(20) AS (data->>'$.status') STORED - 再对
status_val建普通索引:CREATE INDEX idx_status ON mytable(status_val) - 查询时直接用
WHERE status_val = 'active',就能命中索引 - 注意:生成列必须是
STORED(非VIRTUAL),否则无法索引
这个点最容易被忽略:开发时数据量小看不出问题,等表涨到百万级,一个简单 JSON 字段查询就拖垮整个服务。











