json_table 是 mysql 8.0+ 将 json 数据虚拟为关系表进行查询的函数,解决结构化展开问题;需明确定义列路径、类型与来源,适用于字段稳定、可枚举的 json,不支持动态键或深层嵌套。

JSON_TABLE 是什么,它能解决什么问题
JSON_TABLE 不是把 JSON 字符串“解析”完事,而是把它当作一个虚拟表来查询——你得明确告诉 MySQL 每一列从哪取、怎么取、类型是什么。它适合处理结构相对稳定、字段可枚举的 JSON 数据,比如日志片段、配置项、API 返回的嵌套对象。如果 JSON 层级太深、字段名动态变化(比如 user_123、user_456 这种),JSON_TABLE 就无能为力了,得换 JSON_EXTRACT + 应用层拼接。
基础语法:必须写全四个部分
JSON_TABLE 的调用必须包含:源 JSON 表达式、别名、COLUMNS 子句、以及至少一个列定义。漏掉任何一部分都会报错,常见错误是忘记写 COLUMNS 或者列定义里漏了 FOR ORDINALITY / PATH。
-
JSON_TABLE('{"id": 1, "name": "Alice"}', '$' COLUMNS (id INT PATH '$.id', name VARCHAR(20) PATH '$.name')) AS jt—— 这是最小合法形式 - 路径必须用单引号包裹的字符串,不能写成
$.id(没引号)或"$.id"(双引号在某些客户端会出问题) - 每个列定义必须指定类型,
VARCHAR要带长度,INT不能省略,否则报ERROR 3148 (HY000): Invalid data type for column 'xxx' - 如果源 JSON 是数组,
'$[*]'是标准写法;写成'$'会只取根对象,数组会被当单个值处理,结果只有一行
处理 JSON 数组:PATH 和 FOR ORDINALITY 的配合
多数真实场景中你要展开的是 JSON 数组(如 [{"a":1},{"a":2}]),这时 PATH '$[*]' 是关键,它让 JSON_TABLE 对数组每个元素生成一行。但光这样还不够——你需要区分行序,否则无法关联原始上下文。
- 加
rownum FOR ORDINALITY可以生成递增序号,类型固定为UNSIGNED INTEGER,不能改 - 如果数组元素本身有嵌套,比如
{"user": {"id": 101, "tags": ["a","b"]}},要展开tags数组就得再套一层JSON_TABLE,不能在一个COLUMNS里直接展开多层数组 - 注意空数组:
[]会返回 0 行;[null]会返回 1 行,所有列都是 NULL;[{}]则各列按类型默认值填充(如INT为 0,VARCHAR为空字符串)
性能和兼容性陷阱
JSON_TABLE 在 MySQL 8.0.4 引入,8.0.20+ 才支持 NESTED PATH(用于多层嵌套展开)。低于这个版本遇到嵌套数组就得手动 JOIN 多次 JSON_TABLE,非常啰嗦。
- 它不走索引——哪怕你在 JSON 列上建了
GENERATED COLUMN + INDEX,JSON_TABLE本身仍会全表扫描源表 - 对大 JSON 文本(> 1MB)性能急剧下降,MySQL 会把整个 JSON 加载进内存解析,容易触发
max_allowed_packet限制 - 别在 WHERE 里对
JSON_TABLE输出列做函数操作,比如WHERE UPPER(name) = 'ALICE',这会让优化器放弃可能的下推,先展开再过滤 - 如果只是想查某个键是否存在,用
JSON_CONTAINS或JSON_EXTRACT更快,JSON_TABLE是为“展开成行”而生的,不是为条件判断
真正麻烦的是嵌套层级超过两层,或者字段名来自变量——MySQL 不支持在 PATH 中拼接字符串,这种场景只能交给应用层处理。











