首页/数据/SQL 速查/JSON_MERGE_PATCH / MERGE_PRESERVE 与 || 合并
函数速查60 条

JSON_MERGE_PATCH / MERGE_PRESERVE 与 || 合并

合并两个 JSON 文档:MySQL 提供 patch(RFC 7396 风格)与 preserve 两种;PostgreSQL 用 || 做“右值覆盖”式浅合并。

SQL 速查数据更新于 2026-10-13

JSON 文档合并的两种语义

把两个 JSON 文档合起来有两种截然不同的语义:一种是按补丁规则覆盖,空值表示删除字段,嵌套对象递归合并;另一种是保留式合并,数组拼接、重复键都留下。它们对同一组输入会给出完全不同的结果,用哪一个取决于业务意图——配置更新走覆盖,标签收集走保留。PostgreSQL 侧只有并列运算符,做的是顶层合并,右侧同名字段直接覆盖,嵌套对象整体替换。

基本信息

适用平台MySQL 8.0 / PostgreSQL 12 及以上
MySQL 覆盖式按补丁语义:空值删键、嵌套对象递归合并、非对象直接替换
MySQL 保留式数组拼接、重复键都保留,是旧函数的等价物
PostgreSQL 写法并列运算符,顶层合并,右侧覆盖同名键
版本提示旧的合并函数自 8.0.3 起废弃,需按语义替换

典型使用场景

  • 配置增量更新:只提交变化的字段,其余保持原值
  • 标签或数组收集:多次追加同一字段的值
  • 接口参数覆盖:默认配置与请求参数合并
  • 排查数据差异:确认某次合并到底是覆盖还是追加
  • 从旧版函数迁移:按当前语义选择覆盖或保留

两种语义的结果差异

两种语义的结果差异
-- MySQL 覆盖式:空值删除键,嵌套对象递归合并
SELECT JSON_MERGE_PATCH('{"a":1,"b":2}', '{"a":null,"c":3}');
-- 结果:{"b": 2, "c": 3}    ← a 被删除

-- MySQL 保留式:数组拼接,重复键都保留
SELECT JSON_MERGE_PRESERVE('{"a":[1,2]}', '{"a":[3]}');
-- 结果:{"a": [1, 2, 3]}

-- PostgreSQL:并列运算符做顶层合并,右侧覆盖同名键
SELECT '{"a":1,"b":2}'::jsonb || '{"a":9,"c":3}'::jsonb;
-- 结果:{"a": 9, "b": 2, "c": 3}

-- 注意:嵌套对象是整体替换,不会递归合并
SELECT '{"cfg":{"x":1,"y":2}}'::jsonb || '{"cfg":{"x":9}}'::jsonb;
-- 结果中 cfg 只剩 x,y 消失

语义对照

输入情况覆盖式合并保留式合并
同名字段右侧覆盖左侧两个值都保留
空值字段删除该键保留为空值
嵌套对象递归合并递归合并
数组字段右侧替换左侧数组拼接
非对象值右侧直接替换按保留规则组合
把覆盖式合并当「深度更新」用要格外小心:嵌套对象确实会递归合并,但显式写空值表示删除整个键。提交参数前要确认空值的含义是「删除」还是「不修改」,两者差一个字段就会丢配置。
PostgreSQL 的并列运算符是顶层合并,嵌套对象整体替换。需要递归语义时得在应用层处理,或用支持路径更新的函数逐个字段改写。

易错点

  • 把覆盖式合并当成「字段级更新」:显式空值会删掉整个键,配置项静默丢失
  • PostgreSQL 的并列运算符嵌套对象整体替换,误以为会递归合并,导致子字段被清空
  • 继续使用已废弃的合并函数:会有弃用提示,且语义容易与新的两种函数混淆
  • 数组字段的合并方向搞反,期望追加却得到替换
  • 合并结果不做校验直接写回:结构不符合预期时下游解析失败

数据来源与参考

  • MySQL 8.0 Reference Manual · JSON Functions
  • PostgreSQL Documentation · JSON Functions and Operators
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌