json_table是唯一能真正展开json数组为多行的函数,需用'$[*]'路径遍历数组元素,并在columns中明确定义字段名、类型及提取路径,否则无法实现分组等关系型操作。

JSON_TABLE 是唯一能真正“展开” JSON 数组的函数
MySQL 5.7+ 支持 JSON 类型,但 JSON_EXTRACT 只能取值,不能把一个 [{"a":1},{"a":2},{"a":1}] 变成三行;只有 JSON_TABLE 能做到真正的“行化”——它把数组每个元素映射为一行,后续才能用 GROUP BY。不走这一步,所有分组都是假分组(比如对整个 JSON 字符串 hash 分组)。
必须用 '$[*]' 路径 + 显式 COLUMNS 定义结构
常见错误是只写 JSON_TABLE(json_col, '$') 或漏掉 COLUMNS,结果返回空或报错 Invalid JSON path expression。正确写法必须包含两部分:
-
'$[*]':表示遍历根数组的每个元素(不是'$',也不是'$.items[*]'除非你确定字段名是items) -
COLUMNS(...):显式声明要提取哪些字段、类型和路径,例如name VARCHAR(50) PATH '$.name' - 如果数组元素是纯值(如
["a","b","a"]),要用value VARCHAR(50) PATH '$',不能写'$.name'
分组前务必确认数据可比性:NULL 和类型要一致
JSON_TABLE 展开后,若某元素缺失 name 字段,对应行该列就是 NULL;而 NULL 在 GROUP BY 中会被单独归为一组。更隐蔽的问题是类型隐式转换:
- 写
age INT PATH '$.age',但实际 JSON 里存的是字符串"25"→ 该行age变成0,和真数字25不同组 - 写
status VARCHAR(10) PATH '$.status',但有的值是true→ MySQL 转成"1",和字符串"active"完全无关 - 建议先用
SELECT ... FROM JSON_TABLE(...) WHERE jt.name IS NOT NULL看一眼展开结果,再加GROUP BY
性能关键:虚拟列 + 索引能避免每次解析
如果高频查询固定路径(如总是按 $.variant_id 分组),不要每次都调 JSON_TABLE。直接在表上加虚拟列并建索引:
ALTER TABLE orders ADD COLUMN variant_id INT AS (JSON_EXTRACT(order_summary, '$.products[0].variant_id')) STORED; CREATE INDEX idx_variant_id ON orders(variant_id);
注意:STORED 才能建索引;[0] 只适用于单元素场景;若要取数组中所有 variant_id,仍得靠 JSON_TABLE ——虚拟列无法表达“每个数组元素”。
最易被忽略的一点:JSON 数组长度影响 JSON_TABLE 性能,100 元素的数组会生成 100 行,再分组就变成聚合大表。上线前务必用 EXPLAIN FORMAT=TREE 看执行计划里有没有 JSON_TABLE 的物化步骤,以及是否触发临时表或 filesort。











