JSON_REPLACE仅更新已存在键,不新增不删除,路径必须精确且值需加引号;若键不存在则静默失败,需确认业务是否接受“无操作”。

MySQL 8.0+ 用 JSON_REPLACE 更新单个属性
MySQL 8.0 开始原生支持 JSON 函数,JSON_REPLACE 是最直接的方式:它只修改已存在的键,不新增、不删其他字段,也不会触发类型转换。适合“精准打点”式更新。
常见错误是误用 JSON_SET(它会强制创建键,哪怕原键不存在)或 JSON_INSERT(只插入,不覆盖),导致意外行为。
- 语法:
UPDATE table_name SET json_col = JSON_REPLACE(json_col, '$.key', 'new_value') WHERE id = 1; - 路径必须精确匹配,
$.user.name和$.user["name"]等价,但$.user.name.(多一个点)会静默失败 - 如果
$.key不存在,JSON_REPLACE不做任何事,整条记录保持不变 —— 这是安全的,但需确认业务是否期望这种“无操作” - 注意字符串值要加引号:写成
'"active"(外层单引号,内层双引号)而非'active',否则会被当成标识符报错
PostgreSQL 用 jsonb_set 更新嵌套字段
PostgreSQL 的 jsonb_set 更灵活,但也更易出错:路径参数是文本数组(如 '{user,name}'),不是 JSONPath 字符串;且默认行为是“不存在则插入”,需显式传 false 才限制为仅替换。
- 只更新存在字段:
UPDATE tbl SET data = jsonb_set(data, '{user,role}', '"admin"', false) WHERE id = 1; - 第三个参数必须是
jsonb类型,所以字符串要用'"admin"'(带双引号),数字用'42',布尔用'true' - 路径数组中任意一级为空(如
{user,})会导致整个函数返回 NULL,务必校验字段层级是否完整 - 若目标字段是顶层键,路径写成
'{status}',不是'$.status'—— PostgreSQL 不认 JSONPath 语法
SQLite 3.38+ 用 json_set 需谨慎处理空值
SQLite 从 3.38 起支持 json_set,但它没有“仅替换不新增”的开关,行为类似 PostgreSQL 的 jsonb_set(..., true)。这意味着:如果路径中间某级不存在,它会自动补 null 对象,可能污染原始结构。
- 示例:
UPDATE items SET config = json_set(config, '$.settings.theme', '"dark"') WHERE id = 1; - 如果原 JSON 是
{"settings":{}},结果正常;但如果原 JSON 是{},结果变成{"settings":null,"settings":{"theme":"dark"}}—— 出现重复键和 null 值 - 规避方法:先用
json_type(config, '$.settings')判断路径是否存在,再决定是否更新;或改用json_insert+json_remove组合(更重,但可控) - 所有路径必须用
$.开头,json_set不接受数组式路径
通用陷阱:NULL、类型不一致与索引失效
跨数据库共性问题是:一旦 JSON 字段参与 WHERE 或 UPDATE 条件,很容易因隐式转换或 NULL 处理翻车。这些坑不体现在语法上,却让语句“看似成功实则无效”。
-
WHERE JSON_EXTRACT(json_col, '$.id') = 123在 MySQL 中可能因类型比较失败(JSON 数字 vs SQL 整数),应写成= CAST(123 AS JSON)或用JSON_CONTAINS - PostgreSQL 中
data->'user'->>'name'返回 text,但若原始 JSON 是"name": null,结果是空字符串而非 NULL,导致IS NULL判断失效 - 所有数据库中,对 JSON 字段做函数计算后无法走普通 B-tree 索引;如需高频查询某属性,必须建生成列 + 索引(MySQL)或表达式索引(PostgreSQL)
- SQLite 的
json_valid()应该在 UPDATE 前检查,否则json_set可能静默返回 NULL,而你根本不知道原字段已损坏


















