应使用 json_merge_patch() 而非 json_merge_preserve() 更新配置,因后者会将重复键值转为数组导致类型错误;json_merge_patch() 执行整键替换且任一参数为 null 则结果静默为 null,须用 coalesce 防空并确保字段类型为 json。

直接用 JSON_MERGE_PATCH(),但必须确保所有输入都是合法 JSON 且非 NULL —— 否则结果静默为 NULL,配置就丢了。
为什么不能用 JSON_MERGE_PRESERVE() 更新配置
配置更新场景下,你只改了 "theme" 和 "lang",其他字段理应保持原样。但 JSON_MERGE_PRESERVE() 遇到重复键会把值塞进数组:{"retry_count": [3, 5]} 这种结构根本没法读取或校验。
- 它适合日志归档、多版本快照等需要保留历史值的场景,不适合配置覆盖
- 一旦在 UPDATE 中误用,后续应用读取
retry_count时可能报类型错误或取到数组而非数字 - 没有警告,不报错,只悄悄“污染”数据结构
JSON_MERGE_PATCH() 对嵌套对象和数组的真实行为
它不是“合并子字段”,而是“整键替换”:右侧同名键的值(无论对象、数组还是字符串)直接覆盖左侧整个值。
-
SELECT JSON_MERGE_PATCH('{"user": {"id": 1}}', '{"user": {"name": "A"}}')→{"user": {"name": "A"}}(不是{"user": {"id": 1, "name": "A"}}) -
SELECT JSON_MERGE_PATCH('{"list": [1,2]}', '{"list": 42}')→{"list": 42}(数组被替换成数字,不是报错) -
SELECT JSON_MERGE_PATCH('{"x": [1,2]}', '{"x": ["a","b"]}') → {"x": ["a","b"]}(数组被整个替换,不是拼接)
实际 UPDATE 语句中必须检查的三件事
直接写 JSON_MERGE_PATCH(config, '{"log_level":"debug"}') 很危险,运行前确认:
- 字段类型是
JSON:用SHOW COLUMNS FROM your_table LIKE 'config'查,不是VARCHAR或TEXT - 存量数据通过
JSON_VALID(config):加 WHERE 子句过滤掉非法 JSON,例如WHERE JSON_VALID(config) - 补丁内容是字符串字面量或表达式,不是未转义变量:不要拼接用户输入,避免注入;用参数化查询或
JSON_OBJECT()构造安全补丁
NULL 值会让整个合并失效
JSON_MERGE_PATCH() 只要任一参数为 NULL,结果就是 NULL —— 不报错,也不提示,UPDATE 后配置字段直接清空。
- 常见于 LEFT JOIN 后某列为空,或默认值设为
NULL的 JSON 字段 - 安全写法:用
COALESCE(config, '{}')替换左侧,用COALESCE(patch_json, '{}')替换右侧 - 示例:
UPDATE users SET config = JSON_MERGE_PATCH(COALESCE(config, '{}'), '{"timeout": 30}') WHERE id = 123
最易被忽略的是字段类型和 NULL 处理 —— 表里看着像 JSON,可能是 VARCHAR 存的字符串;线上 UPDATE 看似成功,实际因 NULL 导致配置被置空,问题往往延后暴露。











