前言:MySQL 5.7开始原生支持JSON字段,提供了一套专门的JSON函数用来局部更新JSON,不推荐直接把整个JSON字符串覆盖写入,尤其是JSON对象比较大的时候,局部修改性能更好。本文演示常用更新语法、数组操作、生成列注意事项以及踩坑点。
一、准备测试表
sql
CREATE TABLE user_info (
id INT PRIMARY KEY AUTO_INCREMENT,
ext JSON COMMENT '扩展信息'
);
-- 插入测试数据
INSERT INTO user_info(ext) VALUES ('{"name":"张三","age":20,"tags":["开发","后端"]}');
二、JSON_SET:最常用,新增/更新字段
JSON_SET(json_doc, path, val, ...)
规则:路径存在就更新 ,不存在就新增,适合日常修改JSON对象。
sql
-- 修改name,新增phone字段
UPDATE user_info
SET ext = JSON_SET(ext, '$.name', '张三三', '$.phone', '13800138000')
WHERE id = 1;
查询验证:
sql
SELECT ext FROM user_info WHERE id = 1;
三、JSON_REPLACE:只更新已有字段,不会新增
JSON_REPLACE(json_doc, path, val,...)
路径存在才修改;路径不存在,不做任何操作。
sql
-- 只修改age,尝试修改不存在的email不会生效
UPDATE user_info
SET ext = JSON_REPLACE(ext, '$.age', 22, '$.email', 'test@demo.com')
WHERE id = 1;
四、JSON_INSERT:只新增字段,不会覆盖已有值
JSON_INSERT(json_doc, path, val,...)
路径不存在才插入;已经存在则跳过,不会覆盖原有数据。
sql
-- 新增email,name已存在不会被修改
UPDATE user_info
SET ext = JSON_INSERT(ext, '$.email', 'test@demo.com', '$.name', '李四')
WHERE id = 1;
✅ 三者简单区分
- JSON_SET:有则改,无则加(日常首选)
- JSON_REPLACE:只改已有,不加新key
- JSON_INSERT:只加新key,不改旧值
五、删除JSON内key:JSON_REMOVE
删除JSON对象中的某个key,或者数组指定下标元素
sql
-- 删除phone字段
UPDATE user_info
SET ext = JSON_REMOVE(ext, '$.phone')
WHERE id = 1;
六、JSON数组修改操作
测试数据:"tags":["开发","后端"]
- 追加元素 JSON_ARRAY_APPEND
sql
UPDATE user_info
SET ext = JSON_ARRAY_APPEND(ext, '$.tags', 'MySQL')
WHERE id = 1;
- 在数组开头插入 JSON_ARRAY_INSERT
sql
-- 在数组下标0位置插入元素
UPDATE user_info
SET ext = JSON_ARRAY_INSERT(ext, '$.tags[0]', '全栈')
WHERE id = 1;
⚠️ 注意:数组下标从0开始;数组元素删除同样用 JSON_REMOVE
$.tags[0]
七、批量多路径更新
一条语句同时修改多个key,减少update次数
sql
UPDATE user_info
SET ext = JSON_SET(ext,
'$.name', '王五',
'$.age', 25,
'$.address.city', '重庆'
) WHERE id = 1;
八、JSON字段更新 + 索引相关重点
如果基于JSON字段创建生成列索引 ,更新JSON会同步更新生成列,和普通字段更新一样,需要维护索引成本。
示例:
sql
-- 创建生成列并加索引
ALTER TABLE user_info ADD COLUMN user_name VARCHAR(50) GENERATED ALWAYS AS (ext->>'$.name') STORED;
CREATE INDEX idx_username ON user_info(user_name);
更新ext里的name时,生成列自动同步,索引自动维护,不需要手动操作生成列。
九、常见坑点
-
路径语法错误
路径
$代表根节点,$.key对象属性;数组$.arr[0],写错路径不会报错,但不会生效。->>是取出不带引号的字符串,->取出带双引号JSON字符串,写update path不要混用。 -
不要直接字符串覆盖
sql
-- ❌ 不推荐,整个JSON重写,大JSON性能差
UPDATE user_info SET ext='{"name":"xxx"}' WHERE id=1;
-- ✅ 推荐局部修改 JSON_SET
-
JSON函数返回新JSON,必须赋值回ext
SET ext = JSON_SET(ext, ...)不能省略ext=,很多新手踩坑。 -
NULL的处理
如果ext字段本身是NULL,JSON_SET会返回NULL,不会自动创建JSON对象。
解决方案:使用
JSON_OBJECT()兜底
sql
UPDATE user_info
SET ext = JSON_SET(IFNULL(ext, JSON_OBJECT()), '$.name', '测试')
WHERE id = 1;
- 事务与锁
JSON字段更新属于行更新,InnoDB会加行锁,高并发场景下频繁更新JSON大字段容易产生锁等待。
十、什么时候不建议使用JSON字段更新
- 需要频繁检索JSON内部key,大量查询:优先拆成独立字段
- 超大JSON文档(几KB以上),频繁局部更新:MySQL JSON存储会产生碎片,建议应用层处理