MySQL 8.0 不支持自定义函数递归调用,报错 ERROR 1424;JSON_TABLE 是解析多层 JSON 的可靠方案,需手动展开每层并用 LEFT JOIN 关联,配合 $[*] 展开数组,且须确保 JSON 有效。

JSON_DEPTH 和 JSON_LENGTH 能帮你判断结构,但 MySQL 8.0 不支持在自定义函数(FUNCTION)中递归调用自身——这是硬性限制,不是写法问题。你写一个带 SELECT 或 CALL 的函数去调自己,MySQL 会直接报错:ERROR 1424 (HY000): Recursive stored functions and triggers are not allowed.
别浪费时间试 CREATE FUNCTION ... BEGIN ... CALL this_func(...) END,它永远过不了语法校验。
JSON_TABLE 是真正能落地的“解析JSON层级”的方案
你要的“递归解析JSON层级”,本质是把嵌套的 JSON 数组或对象展开成多行关系数据,比如:
{"name": "A", "children": [{"name": "B", "children": [{"name": "C"}]}]}想变成三行:A、B、C,并带层级和路径。这不是靠函数一层层 JSON_EXTRACT 能优雅解决的。
JSON_TABLE 正是为此而生,它支持路径表达式 + 嵌套 COLUMNS,天然适配树形 JSON:
- 支持递归式路径(如
$<strong>.name</strong>),但注意:MySQL 的是 非标准扩展,仅匹配直接子元素,不真正递归遍历任意深度(这点常被误读) - 真正可靠的写法是:明确写出每层路径,配合
LEFT JOIN JSON_TABLE(...)多次展开
示例(解析最多3层的菜单 JSON):
SELECT jt1.name AS level1, jt2.name AS level2, jt3.name AS level3 FROM menu_config m CROSS JOIN JSON_TABLE(m.data, '$' COLUMNS ( name VARCHAR(100) PATH '$.name', children JSON PATH '$.children' )) jt1 LEFT JOIN JSON_TABLE(jt1.children, '$[*]' COLUMNS ( name VARCHAR(100) PATH '$.name', children JSON PATH '$.children' )) jt2 ON jt1.children IS NOT NULL LEFT JOIN JSON_TABLE(jt2.children, '$[*]' COLUMNS ( name VARCHAR(100) PATH '$.name' )) jt3 ON jt2.children IS NOT NULL;
要点:
- 每层
JSON_TABLE对应一个固定层级,不能靠“自动递归” -
LEFT JOIN保证浅层数据不丢 -
$[*]是数组展开关键,不是$** - 如果 JSON 层级超过3层,就得手动加第4个
JOIN—— 这就是“有限展开”的代价
为什么不用自定义函数 + 循环拼接?
你可以写存储过程(PROCEDURE)配合临时表 + WHILE 循环模拟递归,但:
- 存储过程不能在普通
SELECT中调用,只能CALL后查临时表,无法嵌入报表或视图 - 每次调用都要建/删临时表,高并发下容易锁表或命名冲突
- JSON 解析逻辑全在 SQL 里,调试困难,出错信息模糊(比如路径不存在时返回
NULL而非报错)
而 JSON_TABLE 是声明式语法,错误在执行时立刻暴露(如路径无效会报 Invalid path expression),且可直接用于 JOIN、WHERE、聚合,和普通表无异。
容易忽略的坑:JSON 字段类型 vs 字符串
JSON_TABLE 接收的是 JSON 文档(JSON 类型或合法 JSON 字符串),但如果字段是 VARCHAR 且内容含多余空格、换行、单引号,就会失败。
验证方法:
SELECT id, data, JSON_VALID(data) FROM menu_config WHERE JSON_VALID(data) = 0;
修复建议:
- 插入前用
JSON_SET或应用层确保格式正确 - 查询时加
WHERE JSON_VALID(data)过滤脏数据 - 别依赖
TRIM()或REPLACE()修 JSON 字符串——JSON 解析器对空白敏感,REPLACE(data, '\n', '')可能破坏字符串内换行
真正的递归解析需求,往往意味着层级不确定、路径不可穷举。这时候该考虑的不是“怎么在 MySQL 里硬刚”,而是:这部分逻辑是否更适合交给应用层(如 Go 的 json.RawMessage 或 Python 的 jsonpath-ng)?数据库只存原始 JSON,解析交给更灵活的环境。


















