JSON_EXTRACT可精准提取嵌套字段,路径须以$开头、键含空格需双引号、数组下标从0开始;返回带引号JSON值,比较前需JSON_UNQUOTE或用->>操作符,路径错误或越界均返回NULL。

MySQL 5.7+ 如何用 JSON_EXTRACT 定位嵌套字段值
MySQL 原生支持 JSON 字段,但直接写 WHERE json_col->"$.user.profile.name" 看似方便,实际在 WHERE 条件中会触发全表扫描——因为该操作无法走索引。真正可下推、可监控的起点是 JSON_EXTRACT 配合生成列(generated column)+ 普通索引。
常见错误:用 json_col->>"$.data.items[0].id" 在大表上做条件查询,执行计划显示 type: ALL,响应从毫秒级变成秒级。
- 路径表达式必须用双引号包裹,且不能含变量(如拼接字符串),否则语法报错
Invalid JSON path expression -
->返回带引号的 JSON 字符串,->>自动去引号转为标量,监控数值/布尔变更时务必用后者 - 数组下标越界不报错,而是返回
NULL,比如JSON_EXTRACT('{"a":[1,2]}', "$.a[99]")→NULL,容易掩盖数据结构异常
PostgreSQL 怎么用 jsonb_path_exists 检测深层属性是否存在
PostgreSQL 的 jsonb 类型更适配监控场景,jsonb_path_exists 可精准判断某条路径是否“有值”(非 null 且非缺失),比反复 #>> '{a,b}' 更可靠。
典型使用场景:监控订单 JSONB 字段中 payment.method 是否从 "alipay" 变更为 "wxpay",需先确认该路径始终存在,否则变更对比无意义。
- 路径写法用 SQL/JSON 路径语言,例如
$ ? (@.user.age > 18),注意括号和空格不可省略 - 函数返回
boolean,可直接用于 WHERE 或触发器条件,但不能索引——要加速得建jsonb_path_opsGIN 索引 - 对
null值敏感:jsonb_path_exists('{"x":null}', '$.x')返回true;而'{"x":null}' #>> '{x}'返回""(空字符串)
如何用触发器捕获 JSON 字段内指定路径的值变更(MySQL / PG 通用思路)
直接监听整个 JSON 字段变化没意义——99% 的业务只关心 status、price、updated_by 这几个键。触发器里解析并比对路径值,才是轻量可控的方案。
MySQL 示例(BEFORE UPDATE):
SET @old_status = JSON_UNQUOTE(JSON_EXTRACT(OLD.data, "$.status")); SET @new_status = JSON_UNQUOTE(JSON_EXTRACT(NEW.data, "$.status")); IF @old_status != @new_status THEN INSERT INTO audit_log(...) VALUES (...); END IF;
PostgreSQL 示例(触发器函数):
IF OLD.data #>> '{status}' != NEW.data #>> '{status}' THEN
INSERT INTO audit_log ...;
END IF;
- MySQL 中
JSON_EXTRACT返回 JSON 类型,必须JSON_UNQUOTE才能做字符串比较,否则'"done"' != '"done"'会误判 - PG 中
#>>直接返回 text,但要注意空值:用COALESCE(OLD.data #>> '{x}', '')避免NULL != 'val'判定失败 - 触发器内避免调用
JSON_CONTAINS_PATH多次——每多一次路径检查,性能下降约 15%,优先用COALESCE或默认值兜底
为什么别用应用层解析 JSON 做变更比对
看似灵活,实则埋雷:JSON 字段在数据库里是紧凑格式(无空格、无换行),但应用层反序列化后可能因库差异自动重排 key 顺序,或把 123 当 int、"123" 当 string,导致 {"a":123} != {"a":"123"} 这类误报。
更隐蔽的问题是时序:应用读取旧值 → 处理逻辑 → 写入新值,中间若被其他事务覆盖,你比对的就不是“本次变更”,而是“两次变更的叠加结果”。数据库层触发器天然具备原子性上下文。
- 如果必须用应用层(如审计日志需附带用户上下文),至少用
JSON_DETAILED(MySQL 8.0.29+)或jsonb_pretty(PG)生成规范格式再哈希比对 - 永远不要信任
JSON.stringify(old) === JSON.stringify(new)—— 对象 key 顺序、浮点数精度、null与空字符串混用都会翻车 - 生产环境发现过因 Go 的
json.Marshal把int64(0)序列化为"0",而 MySQL 存的是数字0,导致变更监控漏报


















