前言
在传统数据库设计中,面对动态扩展属性、不规则字段、多标签、复杂附属信息 这类场景,我们通常会使用VARCHAR、TEXT文本字段存储结构化字符串,不仅无法做格式校验,查询、修改内部数据也极其繁琐,还存在性能短板。
MySQL 8.0 对 JSON 数据类型 做了全面增强,原生支持 JSON 格式存储、校验、查询、局部更新,完美适配电商商品动态属性、用户拓展标签、前端配置、物联网数据等新兴业务。本篇从 JSON 基础概念、JSON 路径语法、数据新增、查询、修改、删除、常用函数、实战场景、坑点避坑全方位讲解,搭配大量可直接运行的案例,内容详实
一、本章知识点汇总
- MySQL JSON 数据类型诞生背景、核心优势与适用业务场景
- JSON 基础语法、JSON 路径(JSON Path)标准写法
- JSON 数据创建:
JSON_OBJECT、JSON_ARRAY构造函数 - JSON 数据查询:
JSON_EXTRACT、->、->>三种取值方式 - JSON 数据修改三大核心函数:
JSON_INSERT、JSON_REPLACE、JSON_SET区别与用法 - JSON 数组操作:追加、插入、删除数组元素
- JSON 辅助函数:合法性校验、长度获取、键名提取、数据合并
- JSON 字段结合查询、过滤、分组的综合实战
- JSON 类型与传统 TEXT/VARCHAR 对比、性能优化方案
- 职场高频坑点、版本兼容、生产规范与课后练习题
二、各知识点详解
1. JSON 数据类型介绍
1.1 什么是 MySQL JSON 类型
JSON(JavaScript Object Notation)是轻量级数据交换格式,MySQL 5.7 开始原生支持 JSON 字段,MySQL 8.0 进一步优化了局部更新、索引支持、函数能力 。JSON 字段专门用于存储对象、数组这类半结构化数据。
1.2 相比 TEXT/VARCHAR 的核心优势
- 自动格式校验:存入非法 JSON 格式数据会直接报错,避免脏数据,文本字段无校验能力。
- 原生局部更新:无需取出整条数据修改后全量覆盖,可直接更新 JSON 内部某个键 / 数组元素,性能更高MySQL。
- 内置丰富函数:支持取值、筛选、合并、删除等操作,业务开发更便捷。
- 存储优化:JSON 数据会被解析为 MySQL 内部二进制格式,读取速度远快于文本解析字符串。
1.3 典型适用场景
- 电商商品:动态规格、自定义属性、多标签(不同商品属性不统一)
- 用户系统:用户拓展信息、兴趣标签、设备列表
- 后台配置:页面配置、权限配置、模板参数
- 物联网 / 日志:设备上报的多维度动态数据
- 第三方接口:存储接口返回的标准 JSON 报文
1.4 不推荐使用场景
- 字段固定、结构永久不变(优先使用传统字段,符合三大范式)
- 超高并发、超大存储量的核心业务主表(JSON 索引成本高于普通字段)
2. 前置知识:JSON 与 JSON Path 语法
2.1 基础 JSON 格式
分为两大核心结构:
- JSON 对象 :
{键:值, 键:值},键为字符串,值支持字符串、数字、布尔、数组、嵌套对象 - JSON 数组 :
[元素1,元素2],元素可以是任意类型,数组下标从 0 开始
2.2 JSON Path(路径表达式,核心语法)
Path 用于定位 JSON 内部元素,所有 JSON 函数都依赖该语法,基础规则:
$:代表整个 JSON 文档(根节点),所有路径以$开头$.key:获取 JSON 对象中名为key的字段$[n]:获取数组中下标为n的元素(n 从 0 开始)$.obj.key:获取嵌套对象内的字段$[n].key:获取数组中第 n 个对象的字段
示例
json
{
"username": "张三",
"tags": ["数码", "运动"],
"info": {"age": 25}
}
- 取用户名:
$.username - 取第一个标签:
$.tags[0] - 取年龄:
$.info.age
3. JSON 数据创建与插入
3.1 两种插入方式
方式 1:直接写入 JSON 字符串 (最常用) 方式 2:使用内置构造函数 JSON_OBJECT()(构建对象)、JSON_ARRAY()(构建数组)。
3.2 构造函数语法
sql
-- 构建JSON对象:参数为 键1,值1,键2,值2... 成对出现
JSON_OBJECT(key1,val1,key2,val2...)
-- 构建JSON数组:参数为数组所有元素
JSON_ARRAY(val1,val2,val3...)
4. JSON 查询核心函数与运算符
MySQL 提供三种取值方式,功能相近,写法简洁度不同:
JSON_EXTRACT(json_doc, path):标准函数写法,通用兼容列名 -> path:简写运算符,等价于JSON_EXTRACT,结果带双引号列名 ->> path:终极简写,自动去除字符串两端双引号,职场首选
5. JSON 修改三大核心函数(高频考点)
这三个函数功能相似但逻辑不同,是面试和开发重点区分点:
- JSON_INSERT :只新增,不覆盖。路径不存在则插入新值,路径已存在则不做任何修改MySQL。
- JSON_REPLACE :只覆盖,不新增。路径存在则替换值,路径不存在则不做任何修改MySQL。
- JSON_SET :新增 + 覆盖。路径不存在则新增,路径存在则覆盖,日常开发使用最多。
补充:数组专用操作函数
JSON_ARRAY_APPEND:在数组尾部追加元素JSON_ARRAY_INSERT:在数组指定下标位置插入元素JSON_REMOVE:删除 JSON 中指定路径的元素(对象键 / 数组元素均可)
6. 常用辅助 JSON 函数
JSON_VALID(str):判断字符串是否为合法 JSON,返回 1(合法)/0(非法)。JSON_LENGTH(json_doc):获取 JSON 长度(对象返回键数量,数组返回元素个数)。JSON_KEYS(json_doc):提取 JSON 对象中所有的键名,返回数组。JSON_MERGE_PRESERVE:合并多个 JSON 对象 / 数组,保留所有数据。
三、实战环境准备
创建商品表,模拟电商动态属性场景,包含普通字段 + JSON 拓展属性字段,所有案例基于此表运行:
sql
-- 商品表:普通字段 + JSON类型拓展属性attrs
CREATE TABLE goods (
goods_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品ID',
goods_name VARCHAR(50) NOT NULL COMMENT '商品名称',
price DECIMAL(10,2) NOT NULL COMMENT '商品价格',
attrs JSON COMMENT '动态拓展属性(规格、标签、产地等)'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT '商品信息表';
-- 基础查询语句(通用)
SELECT * FROM goods;
四、应用案例及结果分析
案例 1:JSON 数据插入(两种方式)
方式 1:直接插入 JSON 字符串(推荐,业务常用)
sql
-- 插入JSON对象:包含颜色、产地、标签数组
INSERT INTO goods(goods_name,price,attrs)
VALUES (
'无线鼠标',
89.9,
'{"color":"黑色","origin":"深圳","tags":["办公","数码"]}'
);
方式 2:使用构造函数 JSON_OBJECT + JSON_ARRAY
sql
INSERT INTO goods(goods_name,price,attrs)
VALUES (
'机械键盘',
199,
JSON_OBJECT(
"color","白色",
"origin":"东莞",
"tags",JSON_ARRAY("电竞","外设")
)
);
结果分析
- 两种方式均可正常插入 JSON 数据,效果完全一致。
- 构造函数优势:代码不易出现引号转义错误,适合程序动态拼接数据。
- 非法 JSON(如缺少引号、括号不匹配)插入会直接报错,体现 JSON 字段校验特性。
案例 2:JSON 数据查询(三种取值方式对比)
需求:查询商品名称、价格、颜色、第一个标签、产地。
sql
-- 1. 标准函数写法 JSON_EXTRACT
SELECT
goods_name,
price,
JSON_EXTRACT(attrs, '$.color') AS color,
JSON_EXTRACT(attrs, '$.tags[0]') AS first_tag,
JSON_EXTRACT(attrs, '$.origin') AS origin
FROM goods;
-- 2. 简写运算符 -> (结果带双引号)
SELECT
goods_name,
price,
attrs->'$.color' AS color,
attrs->'$.tags[0]' AS first_tag
FROM goods;
-- 3. 终极简写 ->> (自动去除引号,职场首选)
SELECT
goods_name,
price,
attrs->>'$.color' AS color,
attrs->>'$.tags[0]' AS first_tag
FROM goods;
结果分析
->查询出的字符串会保留双引号,展示不友好;->>自动去除引号,报表、前端对接优先使用。- 数组取值使用
$[下标],下标从 0 开始。 - 支持嵌套路径,例如后续新增
attrs -> '$.detail.weight'可获取嵌套属性。
案例 3:JSON_INSERT 仅新增不覆盖
需求 :给无线鼠标新增weight(重量)字段,已有字段不修改。
sql
UPDATE goods
SET attrs = JSON_INSERT(attrs, '$.weight','80g')
WHERE goods_name = '无线鼠标';
结果分析
- 路径
$.weight原本不存在,成功新增键值对。 - 再次执行相同 SQL,因路径已存在,数据无任何变化 ,符合
JSON_INSERT特性。
案例 4:JSON_REPLACE 仅覆盖不新增
需求:将无线鼠标产地从 "深圳" 修改为 "广州"。
sql
-- 替换已存在的origin字段
UPDATE goods
SET attrs = JSON_REPLACE(attrs, '$.origin','广州')
WHERE goods_name = '无线鼠标';
-- 尝试替换不存在的字段(无效果)
UPDATE goods
SET attrs = JSON_REPLACE(attrs, '$.size','10cm')
WHERE goods_name = '无线鼠标';
结果分析
$.origin存在,值成功被替换。$.size不存在,语句执行但数据无变化,不会新增字段。
案例 5:JSON_SET 新增 + 覆盖(日常主力函数)
需求 :修改鼠标颜色为 "灰色",同时新增size尺寸属性。
sql
UPDATE goods
SET attrs = JSON_SET(
attrs,
'$.color','灰色', -- 已存在,执行覆盖
'$.size','10cm' -- 不存在,执行新增
)
WHERE goods_name = '无线鼠标';
结果分析
- 一条语句同时完成覆盖已有键、新增未知键 ,功能最强,90% 修改场景优先使用
JSON_SET。 - 支持多组
路径+值,从左到右依次执行。
案例 6:JSON 数组操作(追加、插入、删除元素)
6.1 数组尾部追加 JSON_ARRAY_APPEND
需求:给鼠标标签数组末尾新增 "便携" 标签。
sql
UPDATE goods
SET attrs = JSON_ARRAY_APPEND(attrs, '$.tags', '便携')
WHERE goods_name = '无线鼠标';
6.2 数组指定位置插入 JSON_ARRAY_INSERT
需求:在标签数组下标 1 的位置插入 "办公利器"。
sql
UPDATE goods
SET attrs = JSON_ARRAY_INSERT(attrs, '$.tags[1]', '办公利器')
WHERE goods_name = '无线鼠标';
6.3 删除 JSON 元素 JSON_REMOVE
需求 :删除商品的weight重量字段,以及数组中下标 2 的标签。
sql
UPDATE goods
SET attrs = JSON_REMOVE(attrs, '$.weight', '$.tags[2]')
WHERE goods_name = '无线鼠标';
结果分析
- 数组追加、插入区分位置,
APPEND只加尾部,INSERT可指定下标。 JSON_REMOVE支持同时删除多个路径,不存在的路径不会报错。
案例 7:JSON 数据筛选查询(业务高频)
需求 :查询产地为广州、标签包含 "数码" 的商品。
sql
SELECT goods_name, price, attrs->>'$.origin' AS origin
FROM goods
WHERE
attrs->>'$.origin' = '广州'
AND JSON_CONTAINS(attrs, '["数码"]', '$.tags');
结果分析
- 直接用
->>取值后可作为WHERE条件筛选,和普通字段用法一致。 JSON_CONTAINS判断数组中是否包含指定元素,用于标签、列表筛选。
案例 8:辅助函数综合使用
sql
-- 1. 判断是否为合法JSON
SELECT JSON_VALID('{"name":"测试"}') AS is_valid; -- 返回1
SELECT JSON_VALID('{name:测试}') AS is_valid; -- 返回0(非法格式)
-- 2. 获取JSON长度、所有键名
SELECT
attrs,
JSON_LENGTH(attrs) AS json_length,
JSON_KEYS(attrs) AS json_keys
FROM goods WHERE goods_id = 1;
五、注意事项(避坑指南)
- 严格区分三大修改函数 记口诀:INSERT 只加不改,REPLACE 只改不加,SET 又加又改,根据业务场景选择对应函数MySQL。
- JSON Path 书写规范 路径必须以
$开头,数组下标从 0 开始,嵌套层级不要过深(建议≤3 层),否则查询性能下降。 - 引号问题统一用 ->> 做条件筛选、数据展示时,优先使用
->>自动去除引号,避免因多余引号导致匹配失败。 - 禁止滥用 JSON 字段 遵循数据库设计范式,结构固定的字段一律用普通类型,仅动态、不规则数据使用 JSON。
- 局部更新优势利用 MySQL 8.0 对 JSON 函数(
SET/REPLACE/REMOVE)实现局部更新,不要手动全量拼接 JSON 字符串更新,损耗性能MySQL。 - JSON 索引优化 JSON 原生无法直接建索引,高频筛选的 JSON 字段建议创建生成列 + 普通索引。
- 版本兼容问题 MySQL 5.7 仅基础 JSON 能力,8.0 优化数组、局部更新,低版本迁移需测试函数兼容性。
- 数组下标越界 操作数组时,下标超过最大索引不会报错,直接不执行操作,开发时注意下标校验。
- NULL 值处理 JSON 字段允许为 NULL,取值函数对 NULL 字段执行会返回 NULL,业务中做好判空逻辑。
六、核心总结
- JSON 定位:MySQL 8.0 原生半结构化数据类型,适配动态属性、标签、配置等场景,优于 TEXT 文本。
- 核心路径 :JSON Path 以
$为根,$.键取对象,$[下标]取数组,是所有 JSON 操作的基础。 - 查询三写法 :
JSON_EXTRACT(标准)、->(简写带引号)、->>(首选,去引号)。 - 修改三巨头
- JSON_INSERT:纯新增,已有数据不修改;
- JSON_REPLACE:纯替换,不存在数据不新增;
- JSON_SET:新增 + 替换,日常开发主力函数。
- 数组操作 :
JSON_ARRAY_APPEND尾部追加、JSON_ARRAY_INSERT指定位置插入、JSON_REMOVE删除元素。 - 选型规范 :固定字段用普通类型,动态不规则字段用 JSON;查询筛选优先
->>,修改优先JSON_SET。
一句话记忆: JSON 存动态数据,Path 路径是根基;查询优先 ->>,修改分清三大函数;INSERT 只增 REPLACE 只改,SET 全能最常用;数组追加看首尾,合理使用不滥用。
七、练习题(附答案思路)
基于goods商品表完成练习,贴合电商真实业务。
题目 1
向商品表插入一条数据:商品名为 "蓝牙耳机",价格 129,JSON 属性包含brand:"华为"、tags=["音频","无线"]。分别使用字符串方式和构造函数两种写法。
题目 2
查询所有商品,展示商品名、价格、品牌、第二个标签(使用->>写法)。
题目 3
使用JSON_SET,给所有商品统一新增stock:100(库存)属性,并将 "机械键盘" 的产地修改为 "佛山"。
题目 4
删除 "蓝牙耳机" 标签数组中的第一个标签,并删除brand品牌字段。
参考答案思路
题目 1
sql
-- 写法1:直接JSON字符串
INSERT INTO goods(goods_name,price,attrs)
VALUES ('蓝牙耳机',129,'{"brand":"华为","tags":["音频","无线"]}');
-- 写法2:构造函数
INSERT INTO goods(goods_name,price,attrs)
VALUES (
'蓝牙耳机',
129,
JSON_OBJECT("brand","华为","tags",JSON_ARRAY("音频","无线"))
);
题目 2
sql
SELECT
goods_name,
price,
attrs->>'$.brand' AS brand,
attrs->>'$.tags[1]' AS second_tag
FROM goods;
题目 3
sql
-- 新增库存字段
UPDATE goods SET attrs = JSON_SET(attrs, '$.stock', 100);
-- 修改机械键盘产地
UPDATE goods SET attrs = JSON_SET(attrs, '$.origin', '佛山')
WHERE goods_name = '机械键盘';
题目 4
sql
UPDATE goods
SET attrs = JSON_REMOVE(attrs, '$.tags[0]', '$.brand')
WHERE goods_name = '蓝牙耳机';