MySQL JSON 处理学习文档

MySQL JSON 处理学习文档

假设订单表 orderr_orderrcomment 字段

原始数据

sql 复制代码
-- 表结构(简化)
CREATE TABLE orderr_orderr (
    id BIGINT PRIMARY KEY,
    title VARCHAR(100),
    monthly_rent DECIMAL(10,2),
    comment JSON DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 插入数据
INSERT INTO orderr_orderr (id, title, monthly_rent, comment) VALUES
(1011695605988196352, '测试订单A', 120.00,
'{"devNum": 2, "productNum": 1, "addressInfo": {"city": "杭州市", "phone": "13800001111", "address": "西湖区文三路100号天际大厦3幢508室", "village": "", "province": "浙江省", "consignee": "张三"}, "orderrCheckId": "1011695663005564928", "wareDeliveryId": 637, "wareDeliveryStatus": 0}'),

(1011695605988196353, '测试订单B', 200.00,
'{"devNum": 3, "productNum": 2, "addressInfo": {"city": "上海市", "phone": "13900002222", "address": "浦东新区张江高科技园区博云路2号", "village": "", "province": "上海市", "consignee": "李四"}, "orderrCheckId": "1011695663005564929", "wareDeliveryId": 638, "wareDeliveryStatus": 1}'),

(1011695605988196354, '测试订单C', 80.00,
'{"devNum": 1, "productNum": 1, "addressInfo": {"city": "北京市", "phone": "13700003333", "address": "海淀区中关村大街1号海龙大厦12层", "village": "", "province": "北京市", "consignee": "王五"}, "orderrCheckId": "1011695663005564930", "wareDeliveryId": 639, "wareDeliveryStatus": 2}');

comment 字段内容示例(测试订单A):

json 复制代码
{
  "devNum": 2,
  "productNum": 1,
  "addressInfo": {
    "city": "杭州市",
    "phone": "13800001111",
    "address": "西湖区文三路100号天际大厦3幢508室",
    "village": "",
    "province": "浙江省",
    "consignee": "张三"
  },
  "orderrCheckId": "1011695663005564928",
  "wareDeliveryId": 637,
  "wareDeliveryStatus": 0
}

一、查询(SELECT)

1.1 基本查询 --- ->> 操作符(推荐)

sql 复制代码
-- 查询一级字段
SELECT
    id,
    title,
    comment->>'$.devNum'             AS devNum,
    comment->>'$.productNum'         AS productNum,
    comment->>'$.wareDeliveryId'     AS wareDeliveryId,
    comment->>'$.wareDeliveryStatus' AS wareDeliveryStatus
FROM orderr_orderr;
id title devNum productNum wareDeliveryId wareDeliveryStatus
1011695605988196352 测试订单A 2 1 637 0
1011695605988196353 测试订单B 3 2 638 1
1011695605988196354 测试订单C 1 1 639 2

->> 返回的是不带引号的纯文本值,大多数场景用它。

1.2 查询嵌套字段

sql 复制代码
-- 查询 addressInfo 内部的字段
SELECT
    id,
    title,
    comment->>'$.addressInfo.consignee' AS 收货人,
    comment->>'$.addressInfo.phone'     AS 电话,
    comment->>'$.addressInfo.province'  AS 省份,
    comment->>'$.addressInfo.city'      AS 城市,
    comment->>'$.addressInfo.address'   AS 详细地址
FROM orderr_orderr;
id title 收货人 电话 省份 城市 详细地址
...352 测试订单A 张三 13800001111 浙江省 杭州市 西湖区文三路100号天际大厦3幢508室
...353 测试订单B 李四 13900002222 上海市 上海市 浦东新区张江高科技园区博云路2号
...354 测试订单C 王五 13700003333 北京市 北京市 海淀区中关村大街1号海龙大厦12层

1.3 -> vs ->> 的区别

sql 复制代码
-- -> 返回带双引号的 JSON 值
SELECT comment->'$.devNum' FROM orderr_orderr WHERE id = 1011695605988196352;
-- 结果: "2"   (字符串类型)

-- ->> 返回不带引号的纯文本
SELECT comment->>'$.devNum' FROM orderr_orderr WHERE id = 1011695605988196352;
-- 结果: 2     (文本类型)

对比:

操作符 返回值 适用场景
-> JSON 值(带引号) 需要保持 JSON 格式时
->> 文本值(不带引号) 绝大多数场景,推荐使用

1.4 使用 JSON_EXTRACT() 函数

sql 复制代码
-- 等价于 ->
SELECT JSON_EXTRACT(comment, '$.devNum') FROM orderr_orderr WHERE id = 1011695605988196352;
-- 结果: "2"

-- 等价于 ->>(去掉引号)
SELECT JSON_UNQUOTE(JSON_EXTRACT(comment, '$.devNum')) FROM orderr_orderr WHERE id = 1011695605988196352;
-- 结果: 2

二、条件筛选(WHERE)

2.1 简单条件

sql 复制代码
-- devNum = 2 的订单
SELECT * FROM orderr_orderr
WHERE comment->>'$.devNum' = '2';

2.2 数值比较

sql 复制代码
-- wareDeliveryId > 637 的订单
SELECT id, title, comment->>'$.wareDeliveryId' AS wareDeliveryId
FROM orderr_orderr
WHERE CAST(comment->>'$.wareDeliveryId' AS SIGNED) > 637;

注意->> 返回的是文本,做数值比较时建议用 CAST() 转换。

2.3 嵌套字段条件

sql 复制代码
-- 收货人是"张三"的订单
SELECT * FROM orderr_orderr
WHERE comment->>'$.addressInfo.consignee' = '张三';

-- 手机号以 "138" 开头的订单
SELECT * FROM orderr_orderr
WHERE comment->>'$.addressInfo.phone' LIKE '138%';

-- 城市是"杭州市"的订单
SELECT * FROM orderr_orderr
WHERE comment->>'$.addressInfo.city' = '杭州市';

2.4 多条件组合

sql 复制代码
SELECT id, title,
    comment->>'$.devNum'                  AS devNum,
    comment->>'$.addressInfo.consignee'   AS consignee,
    comment->>'$.addressInfo.city'        AS city
FROM orderr_orderr
WHERE comment->>'$.devNum' = '2'
  AND comment->>'$.wareDeliveryStatus' = '0'
  AND comment->>'$.addressInfo.phone' IS NOT NULL;

2.5 判断字段是否存在

sql 复制代码
-- 查询 addressInfo 字段存在的记录
SELECT * FROM orderr_orderr
WHERE JSON_CONTAINS_PATH(comment, 'one', '$.addressInfo');

-- 查询同时有 devNum 和 productNum 的记录
SELECT * FROM orderr_orderr
WHERE JSON_CONTAINS_PATH(comment, 'all', '$.devNum', '$.productNum');

三、修改(UPDATE)

3.1 JSON_SET() --- 更新已有字段 / 插入新字段

sql 复制代码
-- 更新 devNum 为 5,同时新增一个 remark 字段
UPDATE orderr_orderr
SET comment = JSON_SET(comment,
    '$.devNum', 5,
    '$.remark', '这是备注信息'
)
WHERE id = 1011695605988196352;

执行前:

json 复制代码
{"devNum": 2, "productNum": 1, "addressInfo": {...}}

执行后:

json 复制代码
{"devNum": 5, "productNum": 1, "addressInfo": {...}, "remark": "这是备注信息"}

JSON_SET = 存在就更新,不存在就插入,是最常用的修改函数。

3.2 JSON_INSERT() --- 仅插入不存在的字段

sql 复制代码
-- devNum 已存在,不会被修改;newField 不存在,会被插入
UPDATE orderr_orderr
SET comment = JSON_INSERT(comment,
    '$.devNum', 999,            -- 已存在,忽略
    '$.newField', '新字段值'     -- 不存在,插入
)
WHERE id = 1011695605988196352;

结果: devNum 仍然是 2,但多了 newField。

3.3 JSON_REPLACE() --- 仅更新已存在的字段

sql 复制代码
-- devNum 存在则更新;newField 不存在则忽略
UPDATE orderr_orderr
SET comment = JSON_REPLACE(comment,
    '$.devNum', 10,
    '$.newField', '不会插入'
)
WHERE id = 1011695605988196352;

结果: devNum 变为 10,newField 不会被插入。

3.4 JSON_REMOVE() --- 删除字段

sql 复制代码
-- 删除 wareDeliveryStatus 字段
UPDATE orderr_orderr
SET comment = JSON_REMOVE(comment, '$.wareDeliveryStatus')
WHERE id = 1011695605988196352;

-- 删除嵌套字段
UPDATE orderr_orderr
SET comment = JSON_REMOVE(comment, '$.addressInfo.city')
WHERE id = 1011695605988196352;

3.5 修改嵌套对象中的值

sql 复制代码
-- 修改收货人电话
UPDATE orderr_orderr
SET comment = JSON_SET(comment, '$.addressInfo.phone', '15000001234')
WHERE id = 1011695605988196352;

-- 修改收货地址
UPDATE orderr_orderr
SET comment = JSON_SET(comment, '$.addressInfo.address', '新的详细地址')
WHERE id = 1011695605988196352;

3.6 三个修改函数对比

函数 已存在的字段 不存在的字段 类比
JSON_SET 更新 插入 put
JSON_INSERT 忽略 插入 putIfAbsent
JSON_REPLACE 更新 忽略 replace

四、JSON 数组操作

假设新增一个 tags 数组字段:

{"devNum": 2, "tags": ["电子产品", "杭州", "已发货"]}

4.1 查询数组元素

sql 复制代码
-- 查询第一个标签
SELECT comment->>'$.tags[0]' FROM orderr_orderr;
-- 结果: 电子产品

-- 查询数组长度
SELECT JSON_LENGTH(comment->>'$.tags') AS tag_count FROM orderr_orderr;
-- 结果: 3

4.2 追加数组元素

sql 复制代码
-- 在数组末尾追加
UPDATE orderr_orderr
SET comment = JSON_ARRAY_APPEND(comment, '$.tags', '新标签')
WHERE id = 1011695605988196352;

-- 在数组开头插入
UPDATE orderr_orderr
SET comment = JSON_ARRAY_INSERT(comment, '$.tags[0]', '置顶标签')
WHERE id = 1011695605988196352;

4.3 删除数组元素

sql 复制代码
-- 删除第一个元素
UPDATE orderr_orderr
SET comment = JSON_REMOVE(comment, '$.tags[0]')
WHERE id = 1011695605988196352;

五、判断与验证

sql 复制代码
-- 1. 验证是否为合法 JSON
SELECT JSON_VALID(comment) FROM orderr_orderr WHERE id = 1011695605988196352;
-- 结果: 1 (合法)

-- 2. 获取 JSON 类型
SELECT JSON_TYPE(comment) FROM orderr_orderr WHERE id = 1011695605988196352;
-- 结果: OBJECT

-- 3. 获取 JSON 中 key 的数量
SELECT JSON_LENGTH(comment) FROM orderr_orderr WHERE id = 1011695605988196352;
-- 结果: 6

-- 4. 获取所有 key 名称
SELECT JSON_KEYS(comment) FROM orderr_orderr WHERE id = 1011695605988196352;
-- 结果: ["devNum", "productNum", "addressInfo", "orderrCheckId", "wareDeliveryId", "wareDeliveryStatus"]

-- 5. 判断某个值是否包含在 JSON 中
SELECT JSON_CONTAINS(comment, '2', '$.devNum') FROM orderr_orderr WHERE id = 1011695605988196352;
-- 结果: 1 (存在)

-- 6. 格式化输出
SELECT JSON_PRETTY(comment) FROM orderr_orderr WHERE id = 1011695605988196352;

六、聚合统计

sql 复制代码
-- 统计所有订单的设备总数
SELECT SUM(comment->>'$.devNum') AS total_dev
FROM orderr_orderr;

-- 按 devNum 分组统计
SELECT
    comment->>'$.devNum'   AS dev_num,
    COUNT(*)               AS order_count,
    SUM(monthly_rent)      AS total_rent
FROM orderr_orderr
GROUP BY comment->>'$.devNum';

-- 统计收货省份分布
SELECT
    comment->>'$.addressInfo.province' AS province,
    COUNT(*)                           AS cnt
FROM orderr_orderr
WHERE comment->>'$.addressInfo.province' != ''
GROUP BY comment->>'$.addressInfo.province'
ORDER BY cnt DESC;

-- 统计各城市订单数
SELECT
    comment->>'$.addressInfo.city' AS city,
    COUNT(*)                       AS cnt
FROM orderr_orderr
GROUP BY comment->>'$.addressInfo.city'
ORDER BY cnt DESC;

七、建表与索引优化

7.1 建表时定义 JSON 字段

sql 复制代码
CREATE TABLE orderr_orderr (
    id BIGINT PRIMARY KEY,
    title VARCHAR(100),
    monthly_rent DECIMAL(10,2),
    comment JSON DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

7.2 创建虚拟列 + 索引(重要!)

sql 复制代码
-- 从 JSON 中提取常用字段为虚拟列
ALTER TABLE orderr_orderr
ADD COLUMN dev_num INT
    AS (CAST(comment->>'$.devNum' AS SIGNED)) VIRTUAL,
ADD COLUMN consignee VARCHAR(50)
    AS (comment->>'$.addressInfo.consignee') VIRTUAL,
ADD COLUMN phone VARCHAR(20)
    AS (comment->>'$.addressInfo.phone') VIRTUAL,
ADD COLUMN city VARCHAR(50)
    AS (comment->>'$.addressInfo.city') VIRTUAL;

-- 对虚拟列建索引(大幅提升查询性能)
ALTER TABLE orderr_orderr ADD INDEX idx_devnum (dev_num);
ALTER TABLE orderr_orderr ADD INDEX idx_consignee (consignee);
ALTER TABLE orderr_orderr ADD INDEX idx_phone (phone);
ALTER TABLE orderr_orderr ADD INDEX idx_city (city);

7.3 MySQL 8.0 多值索引(针对数组)

sql 复制代码
-- 如果 JSON 中有数组字段 tags
ALTER TABLE orderr_orderr
ADD INDEX idx_tags ((CAST(comment->>'$.tags' AS CHAR(50) ARRAY)));

-- 查询时使用 MEMBER OF
SELECT * FROM orderr_orderr
WHERE '电子产品' MEMBER OF (comment->>'$.tags');

八、实战场景速查表

场景 SQL
查收货人 SELECT comment->>'$.addressInfo.consignee'
查电话 SELECT comment->>'$.addressInfo.phone'
查城市 SELECT comment->>'$.addressInfo.city'
查设备数 SELECT comment->>'$.devNum'
改电话 JSON_SET(comment, '$.addressInfo.phone', '新号码')
加字段 JSON_SET(comment, '$.newKey', 'value')
删字段 JSON_REMOVE(comment, '$.wareDeliveryStatus')
判断存在 JSON_CONTAINS_PATH(comment, 'one', '$.devNum')
数组追加 JSON_ARRAY_APPEND(comment, '$.tags', 'val')
数组插入 JSON_ARRAY_INSERT(comment, '$.tags[0]', 'val')
格式化 SELECT JSON_PRETTY(comment)
验证JSON SELECT JSON_VALID(comment)
获取类型 SELECT JSON_TYPE(comment)
获取keys SELECT JSON_KEYS(comment)

九、常见注意事项

注意点 说明
类型转换 ->> 返回文本,数值比较需 CAST()
NULL 处理 JSON 字段为 NULL 时,->> 也返回 NULL
索引 直接查 JSON 字段无法走索引,需建虚拟列+索引
版本要求 ->> 需 MySQL 8.0+ ;JSON 类型需 5.7+
空值判断 WHERE comment IS NULL 而非 WHERE comment = '{}'
性能 避免在大表上对 JSON 字段做全表扫描
嵌套层级 建议嵌套不超过 3 层,过深影响可读性和性能

十、函数速查表

分类 函数 说明
查询 ->> 查询值(纯文本,推荐)
-> 查询值(JSON格式)
JSON_EXTRACT() 查询值
修改 JSON_SET() 更新/插入
JSON_INSERT() 仅插入
JSON_REPLACE() 仅更新
JSON_REMOVE() 删除字段
数组 JSON_ARRAY_APPEND() 追加元素
JSON_ARRAY_INSERT() 插入元素
验证 JSON_VALID() 验证合法性
JSON_TYPE() 获取类型
JSON_LENGTH() 获取长度
JSON_KEYS() 获取所有key
JSON_CONTAINS() 判断值存在
JSON_CONTAINS_PATH() 判断路径存在
格式 JSON_PRETTY() 格式化输出
相关推荐
资讯综合1 小时前
一年级数学机构怎么选?与学习阶段相匹配的线上思维课程与线下机构差异分析
学习
M78佐菲1 小时前
51单片机学习笔记(2)
linux·笔记·嵌入式硬件·学习·51单片机
子不语1802 小时前
LabVIEW和PLC通过OPC通信
学习·自动化·plc
写后端的胖头鱼2 小时前
【高频面试题】SQL 查询慢怎么排查
数据库·sql·mysql·oracle·慢查询·高频面试题
萧行之2 小时前
Observable Plot 源码深度解析——4 核心抽象逐项学习
前端·学习·数据可视化
扬大平仔2 小时前
# 小深:用 AgentScope Java 2.0 Harness 做私人助手(上)mysql
java·开发语言·mysql
浔溺2 小时前
al+大数据每日学习笔记37
大数据·笔记·学习
TheBestRucy2 小时前
PyTorch 基本使用学习笔记
pytorch·笔记·学习