MySQL 中查询和操作JSON类型的数据

MySQL 5.7 开始原生支持 JSON 数据类型,8.0 进一步增强了JSON函数、JSON表、生成列索引能力。很多业务场景(扩展属性、动态配置、标签)会选择JSON字段存储,但是很多人只会简单插入,不清楚如何提取、修改、索引JSON内部数据,本文结合实战SQL讲解JSON的增删改查与避坑要点。

一、建表:JSON字段基础定义

创建测试表,使用json类型,不要用varchar代替原生JSON类型。原生JSON会做语法校验,非法JSON无法写入。

sql 复制代码
CREATE TABLE `product` (
  `id` INT PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(100) NOT NULL,
  `ext_info` JSON COMMENT '扩展信息,JSON类型'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

二、插入JSON数据

可以直接写JSON字符串插入,MySQL会自动校验JSON合法性。

sql 复制代码
-- 插入单行JSON
INSERT INTO product(name,ext_info)
VALUES('手机', '{"color":"黑色","price":3999,"tags":["数码","5G"],"attr":{"weight":"200g"}}');

-- 多行插入
INSERT INTO product(name,ext_info)
VALUES
('电脑','{"color":"灰色","price":5999,"tags":["数码","笔记本"],"attr":{"weight":"1.2kg"}}'),
('耳机','{"color":"白色","price":299,"tags":["数码","音频"],"attr":{"weight":"8g"}}');

三、查询JSON数据(核心)

1. -> 和 ->> 提取JSON字段

  • ->:返回JSON类型,保留引号
  • ->>:返回字符串,日常业务查询优先用 ->>,会去掉外层引号
sql 复制代码
-- 提取ext_info中的color
SELECT 
  id,
  name,
  ext_info -> '$.color' AS color_json,
  ext_info ->> '$.color' AS color_str
FROM product;

$代表整个JSON对象,$.key 代表取key字段。

2. 查询JSON数组

JSON数组通过下标取值,下标从0开始:

sql 复制代码
-- 获取tags数组第0个元素
SELECT id, name, ext_info ->> '$.tags[0]' as tag1 FROM product;

3. JSON_CONTAINS 判断是否包含元素

常用于标签数组匹配,查找tags包含数码的商品

sql 复制代码
SELECT id,name,ext_info
FROM product
WHERE JSON_CONTAINS(ext_info->'$.tags', '"数码"');

4. JSON_EXTRACT 函数写法(等价 ->)

sql 复制代码
SELECT JSON_EXTRACT(ext_info, '$.price') FROM product;

四、更新JSON数据

使用JSON_SET、JSON_INSERT、JSON_REPLACE、JSON_ARRAY_APPEND

  1. JSON_SET:存在则更新,不存在则新增(最常用)
sql 复制代码
-- 修改price,新增stock字段
UPDATE product
SET ext_info = JSON_SET(ext_info, '$.price', 3799, '$.stock', 100)
WHERE id=1;
  1. JSON_INSERT:只新增,已有key不修改
  2. JSON_REPLACE:只修改,不存在key不新增
  3. JSON_ARRAY_APPEND:给数组追加元素
sql 复制代码
-- tags数组新增"快充"标签
UPDATE product
SET ext_info = JSON_ARRAY_APPEND(ext_info, '$.tags', "快充")
WHERE id=1;
  1. JSON_REMOVE 删除JSON内指定key
sql 复制代码
-- 删除stock字段
UPDATE product
SET ext_info = JSON_REMOVE(ext_info, '$.stock')
WHERE id=1;

五、JSON生成列 + 索引(重点优化)

原生JSON字段不能直接建索引 ,如果需要根据JSON内部字段快速检索,必须创建生成列,再给生成列加索引。

sql 复制代码
-- 新增生成列,提取price,然后创建索引
ALTER TABLE product ADD COLUMN ext_price INT GENERATED ALWAYS AS (ext_info ->> '$.price') STORED;
CREATE INDEX idx_product_ext_price ON product(ext_price);

之后查询可以直接使用生成列,走索引:

sql 复制代码
SELECT * FROM product WHERE ext_price>3000;

注意:STORED表示实际存储数据,VIRTUAL不存储(默认),InnoDB只能对STORED生成列建立索引。

六、JSON_TABLE:JSON数组转多行(MySQL8.0特有)

如果JSON里是数组,想要一行拆成多条记录,使用JSON_TABLE。

sql 复制代码
SELECT p.id, p.name, jt.tag
FROM product p,
JSON_TABLE(
  p.ext_info -> '$.tags',
  '$[*]' COLUMNS (tag VARCHAR(32) PATH '$')
) jt;

这条SQL会把tags数组的每一个元素单独一行输出,做统计、关联查询非常好用。

七、常用JSON函数汇总

函数 作用
JSON_EXTRACT 提取JSON值
JSON_SET 更新/新增json键
JSON_REMOVE 删除json键
JSON_ARRAY_APPEND 数组追加元素
JSON_CONTAINS 判断包含
JSON_VALID 判断字符串是否合法JSON
JSON_LENGTH 获取JSON数组/对象长度

八、坑点总结

  1. 不要大量在WHERE条件直接写 ext_info->>'$.xxx',没有索引,全表扫描,高频查询字段一定要用生成列索引。
  2. JSON查询大小写敏感,匹配字符串时注意。
  3. MySQL5.7没有JSON_TABLE,数组拆行需要在应用层处理。
  4. JSON字段不支持传统的联合索引,只能针对生成单列建索引。
  5. 复杂JSON嵌套层级很深时,查询性能会下降,核心字段尽量提为独立普通列。

九、选型建议

  • 适合:动态扩展属性、不确定字段、少量查询筛选,数据结构经常变更。
  • 不适合:需要频繁WHERE、JOIN、排序、统计的核心字段,优先单独建列。
相关推荐
达梦数据1 小时前
达梦数据复制软件DMDRS搭建部署示例:源数据库DMDPC到目标数据库DM8一对一数据同步
数据库·oracle
AIgorithmGEEK10 小时前
MySQL 索引全解(下):建、查、删实战与知识速查表
数据库·mysql·b+树·索引
Lost_the_wind10 小时前
MySQL学习笔记四
mysql
唠点键盘之外的12 小时前
26 向量数据库实战:从 0 搭一条能用的检索链路(pgvector + Spring AI)
数据库·人工智能·spring
云和恩墨12 小时前
数据库监控简史:监控不止于告警——从图表大爆发到健康管理闭环
数据库
ly768912 小时前
MongoDB 事务开销实测:快照隔离下的写冲突、重试与分片事务边界
数据库·mongodb·事务·分片集群·快照隔离·写冲突
豆角焖肉12 小时前
Spring AOP 自定义注解实现系统操作日志
数据库·自定义注解·springaop·操作日志·aop 切面
金伟API102414 小时前
不会写复杂SQL怎么办?
数据库·sql
执笔为剑14 小时前
2.1.2-1、V8R3迁移到V8R6-Oracle兼容特性
数据库