直接结论:MySQL 8.0 存储过程中解析复杂 JSON 不能依赖循环调用 JSON_EXTRACT,必须用 JSON_TABLE 一次性展开——因每次 JSON_EXTRACT 均全量解析整棵树、无缓存、不可下推,循环调用导致性能随嵌套深度线性恶化。

直接结论:MySQL 8.0 存储过程中解析复杂 JSON,**不能依赖循环调用 JSON_EXTRACT 拆解嵌套结构**——那会触发多次全量解析,性能随嵌套深度线性恶化;必须用 JSON_TABLE 一次性展开,再配合游标或集合操作处理结果。
为什么存储过程里用 JSON_EXTRACT 解析多层 JSON 会变慢
每次调用 JSON_EXTRACT(data, '$.orders[0].items[1].skuid') 都要从头解析整棵 JSON 树,底层二进制格式虽快,但无缓存复用机制。若在 WHILE 循环中反复调用(比如遍历 orders 数组),CPU 时间全耗在重复解析上,EXPLAIN 看不到执行计划,因为根本没走优化器路径。
- 典型症状:输入 JSON 文档增大 2 倍,执行时间翻 3 倍以上
- 更隐蔽的问题:
JSON_CONTAINS或JSON_SEARCH在存储过程中无法下推,仍需逐行计算 -
JSON_LENGTH返回数组长度后,你仍得靠WHILE i + <code>JSON_EXTRACT(... CONCAT('$[', i, ']'))拼路径——字符串拼接+重复解析,双重开销
用 JSON_TABLE 替代循环解析的实操写法
把 JSON 解析动作“提前固化”到查询阶段,让 MySQL 内核一次性完成结构化展开,结果直接可被游标遍历或 INSERT INTO SELECT 处理。
- 必须显式声明路径:
JSON_TABLE(@json_str, '$.orders[*]',不能省略[*];否则只取第一个元素 - 嵌套数组必须用
NESTED PATH分层,例如 items 在 orders 下:外层COLUMNS(order_id INT PATH '$.id')+ 内层NESTED PATH '$.items[*]' COLUMNS(item_skuid INT PATH '$.skuid') - 每列必须加
ON EMPTY NULL ON ERROR NULL,否则某条记录字段缺失或类型错(如"1001"字符串 vs INT),整行被丢弃,不是填 NULL - 不要在存储过程中对
JSON_TABLE结果再套JSON_EXTRACT—— 展开后的字段已是普通标量,直接用变量接收即可
DECLARE cur_order_id INT;
DECLARE cur_item_skuid INT;
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR
SELECT order_id, item_skuid FROM JSON_TABLE(
@json_str,
'$.orders[*]' COLUMNS (
order_id INT PATH '$.id',
NESTED PATH '$.items[*]' COLUMNS (
item_skuid INT PATH '$.skuid' ON EMPTY NULL ON ERROR NULL
)
)
) AS jt;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
存储过程里建临时表承接 JSON_TABLE 结果更稳
游标对 JSON_TABLE 的支持在某些 MySQL 8.0.x 小版本(如 8.0.12)存在稳定性问题,且调试困难。更推荐先落地为临时表,再分步处理。
- 临时表必须用
ENGINE=InnoDB,避免 MyISAM 在事务中不可见 - 字段类型要严控:状态码用
VARCHAR(20),ID 用INT UNSIGNED,别用TEXT或过宽VARCHAR(255),否则后续 JOIN 或索引失效 - 插入时用
INSERT INTO temp_items SELECT ... FROM JSON_TABLE(...),不要用INSERT ... VALUES (JSON_EXTRACT(...)) - 临时表名避开保留字,比如别叫
order、group,否则语法报错
容易被忽略的兼容性与调试陷阱
JSON 路径语法错误在存储过程中不报具体行号,只抛 ERROR 3143 或 ERROR 3149,定位极难。最稳妥的方式是:先把 JSON_TABLE 查询单独拿出来在客户端执行通,再复制进存储过程。
-
JSON_VALID(@json_str)必须放在开头校验,否则非法 JSON 会导致整个存储过程中断 - MySQL 8.0.13 之前,
JSON_TABLE不支持在子查询中嵌套使用,所以临时表是唯一可靠方案 - 路径中含特殊字符(如
-、.)必须用双引号包裹键名:写成'$.user["first-name"]',不能写'$.user.first-name' - 不要在
JSON_TABLE的COLUMNS中引用上层未定义的字段,比如NESTED PATH '$.items[*]'下写PATH '$.order_id'是非法的,只能用外层已声明的列名


















