拒绝应用层循环!用 MySQL 8.0 JSON_TABLE 一键把 JSON 数组“变”成表

你的 orders 表里存了一个 items 字段,格式是 JSON 数组,记录了订单里的商品详情:
[{"skuid": 1001, "price": 99}, {"skuid": 1002, "price": 50}]
需求: 老板让你统计一下,所有订单中 skuid = 1001 的商品一共卖了多少钱。
痛苦的旧写法:

  1. SELECT items FROM orders; (把全量 JSON 拉到内存)
  2. Java 代码里 JSON.parse(items)
  3. 双层 for 循环遍历订单和商品,累加金额。
    代价: 巨大的网络 I/O 开销,OOM(内存溢出)的风险,数据库计算能力的浪费。

优雅的新写法:

利用 JSON_TABLE,直接在 SQL 里把这个数组"打散"成行,然后 SUM()零网络传输,计算下推到数据库。


1. 核心原理:ETL in SQL

JSON_TABLE 的作用是 Extract(提取)- Transform(转换)- Load(加载) ,但它不是加载到磁盘,而是加载到查询时的临时虚拟表中。

它接收两个核心参数:

  1. JSON 源数据
  2. 映射规则 (COLUMNS):定义如何把 JSON 的 Key 映射为 SQL 的列。

2. 实战演练:从 JSON 数组到 SQL 结果集

准备数据
sql 复制代码
CREATE TABLE order_docs (
    id INT PRIMARY KEY,
    user_name VARCHAR(50),
    items JSON
);

INSERT INTO order_docs VALUES 
(1, 'Alice', '[{"name": "Mouse", "price": 50}, {"name": "Keyboard", "price": 200}]'),
(2, 'Bob',   '[{"name": "Mouse", "price": 50}]');
需求:打平数组,统计销量

我们希望看到这样的表格结果:

user_name product_name price
Alice Mouse 50
Alice Keyboard 200
Bob Mouse 50
SQL 实现 (JSON_TABLE):
sql 复制代码
SELECT 
    o.user_name,
    jt.product_name,
    jt.price
FROM 
    order_docs o,
    -- 核心语法开始
    JSON_TABLE(
        o.items,           -- 1. 数据源
        '$[*]'             -- 2. 路径:遍历数组里的每一个元素
        COLUMNS (
            -- 3. 定义列映射
            product_name VARCHAR(50) PATH '$.name',
            price        DECIMAL(10,2) PATH '$.price',
            -- 甚至可以生成自动递增的序号!
            item_index   FOR ORDINALITY 
        )
    ) AS jt; -- 别忘了给虚拟表起个名字

执行逻辑:

MySQL 会遍历 order_docs 的每一行,针对每一行里的 items JSON,动态生成一张名为 jt 的虚拟表,然后与原表 o 进行 Lateral Join (横向连接)。


3. 三大实战场景

场景一:JSON 数据的聚合统计 (Sum/Count)

这是最直接的收益场景。
需求: 统计所有订单中 "Mouse" 的总销售额。

sql 复制代码
SELECT 
    SUM(jt.price) as total_sales
FROM 
    order_docs o,
    JSON_TABLE(
        o.items, '$[*]' COLUMNS (
            p_name VARCHAR(50) PATH '$.name',
            price DECIMAL(10,2) PATH '$.price'
        )
    ) AS jt
WHERE 
    jt.p_name = 'Mouse';

优势: 相比于把几万条订单拉到应用层处理,这条 SQL 执行速度快几个数量级,且不仅节省网络,还利用了数据库的并行计算能力。

场景二:关联查询 (JSON ID -> 另一张表)

背景: 很多系统为了高性能,会在主表存关联 ID 的 JSON 数组,比如 user_tags 表里存 tag_ids: [101, 102, 105]
需求: 查询用户具备的所有标签的名称 (需要关联 tags 表)。

sql 复制代码
SELECT 
    u.user_name, t.tag_name
FROM 
    users u,
    -- 1. 先把 JSON 里的 [101, 102] 转成行
    JSON_TABLE(u.tag_ids, '$[*]' COLUMNS (
        tid INT PATH '$'
    )) AS jt
-- 2. 再去关联 tags 表
JOIN tags t ON jt.tid = t.id;

这在以前通常需要代码循环查询,现在一条 SQL 搞定。

场景三:导入复杂配置数据 (ETL)

背景: 外部系统发来一个巨大的 JSON 配置文件,包含嵌套的部门和人员信息,需要导入到我们数据库的标准关系表中。
解法: 直接把大 JSON 作为一个参数传给 SQL,利用 JSON_TABLE 解析出多行数据,配合 INSERT INTO ... SELECT ... 批量入库。


4. 注意事项与限制

  1. 版本要求: 必须是 MySQL 8.0+ 。MySQL 5.7 只有 JSON_EXTRACT,不支持 JSON_TABLE
  2. 数据类型匹配:COLUMNS 定义时,类型(如 INT, VARCHAR)要与 JSON 里的实际值匹配,否则可能会报错或产生默认值。
  3. 性能考量: JSON_TABLE 是在查询时实时计算的(On-the-fly)。如果 JSON 非常大(几 MB),或者表数据量极大,CPU 消耗会很高。
  • 优化策: 如果查询极其频繁,建议在写入时就拆分到子表,或者使用 MySQL 8.0 的"函数索引"对提取出的关键字段加索引。

5. 总结

JSON_TABLE 是连接 NoSQL 灵活性和 SQL 规范性的桥梁。

  • 以前: JSON 是个黑盒,只能整体存取,逻辑全靠代码写。
  • 现在: JSON 就是一张待展开的表,SQL 的所有能力(Join, Group By, Window Function)都能直接作用于 JSON 内部数据。

拒绝在应用层写丑陋的 for 循环,把数据处理还给数据库!

相关推荐
困知勉行1985几秒前
Redis数据结构及其底层实现
数据库·redis·缓存
一直在追8 分钟前
告别 WHERE id=1!大数据工程师的 AI 觉醒:手把手带你拆解向量数据库 (RAG 核心)
大数据·数据库
Gofarlic_OMS12 分钟前
协同设计平台中PTC许可证的高效调度策略
网络·数据库·安全·oracle·aigc
刘一说15 分钟前
Windows 与 Linux 跨平台自动化 MySQL 8 备份:专业级脚本设计与实战指南
linux·数据库·windows·mysql·自动化
Wpa.wk30 分钟前
接口自动化 - 解决大量响应数据字段的格式断言 -Json-schema
运维·经验分享·测试工具·自动化·json·接口测试
耶夫斯计38 分钟前
【SQL_agent】基于LLM实现sql助理
数据库·python·sql·语言模型
徐同保1 小时前
使用node清空pinecones向量数据库
数据库
陈逸轩*^_^*1 小时前
软件工程考试速通
数据库·软件工程
Lhan.zzZ1 小时前
Qt绘制残留问题排查与修复日志
开发语言·数据库·qt
Java陈序员1 小时前
数据同步神器!一款搞定多种数据源同步的开源中间件!
java·spring boot·mysql