拒绝应用层循环!用 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 循环,把数据处理还给数据库!

相关推荐
jghhh0115 分钟前
MATLAB分形维数计算:1D/2D/3D图形的盒维数实现
数据库·matlab
weixin_4624462322 分钟前
一键安装 MySQL 5.7(CentOS 7)自动化脚本详解
mysql·centos·自动化
重生之绝世牛码1 小时前
Linux软件安装 —— PostgreSQL高可用集群安装(postgreSQL + repmgr主从复制 + keepalived故障转移)
大数据·linux·运维·数据库·postgresql·软件安装·postgresql高可用
数据知道1 小时前
PostgreSQL 实战:详解 UPSERT(INSERT ON CONFLICT)
数据库·python·postgresql
源力祁老师1 小时前
Odoo日志系统核心组件_logger
网络·数据库·php
电商API&Tina1 小时前
电商API接口的应用与简要分析||taobao|jd|微店
大数据·python·数据分析·json
洋不写bug3 小时前
数据库基础核心操作——CRUD,超详细解析,搭配表格讲解和需求的实现。
数据库
马猴烧酒.3 小时前
JAVA后端用户登录与鉴权详解
java·数据库·sql
heartbeat..3 小时前
Redis 常用命令全解析:基础、进阶与场景化实战
java·数据库·redis·缓存
数据知道3 小时前
PostgreSQL 实战:一文掌握如何优雅的进行递归查询?
大数据·数据库·postgresql