JSON_MODIFY 不能直接更新嵌套属性,因 strict 模式下路径层级缺失会报错,且非 strict 模式可能意外创建结构;安全做法是前置 ISJSON 和 JSON_VALUE 校验,并用 JSON_QUERY 配合 CONCAT 追加数组元素。

JSON_MODIFY 为什么不能直接用 SET column = JSON_MODIFY(...) 更新嵌套属性?
因为 JSON_MODIFY 默认行为是“覆盖式写入”:如果路径不存在,它会创建;但如果路径存在且你传的是 'strict $ .a.b' 这种带 strict 的路径,而 a 本身为 null 或不是对象,就会报错 JSON path is not valid。更常见的是——你以为在改 $.user.name,结果整列 JSON 被意外清空或变成 null,只因原值里 user 字段压根不存在。
实操建议:
- 永远先用
ISJSON(column) = 1和JSON_VALUE(column, '$.user') IS NOT NULL做前置校验,避免语句因单行数据异常而整体失败 - 路径表达式别硬写
strict,除非你 100% 确认层级存在;默认非 strict 模式下,JSON_MODIFY(col, '$.user.name', 'Alice')会在user为null时自动创建对象结构 - 更新前用
SELECT id, column, JSON_VALUE(column, '$.user.name') FROM t WHERE ...抽样验证原始结构,比盲更新安全得多
如何安全地追加数组元素而不覆盖整个数组?
想给 JSON 数组 $.tags 推一个新字符串 'sql',但直接写 JSON_MODIFY(col, '$.tags[0]', 'sql') 会覆盖第一个元素;用 $.tags[999] 又不可靠——SQL Server 不支持负索引或 append 关键字(不像 MySQL 的 JSON_ARRAY_APPEND)。
正确做法是拼接原数组和新值再解析:
UPDATE t
SET json_col = JSON_MODIFY(
json_col,
'$.tags',
JSON_QUERY(CONCAT('[', ISNULL(STRING_AGG(QUOTENAME(tag, '"'), ','), ''), ',"sql"]'))
)
FROM t
CROSS APPLY OPENJSON(json_col, '$.tags') WITH (tag NVARCHAR(100) '$') AS j
WHERE id = 123;
但更轻量、更常用的是利用 JSON_MODIFY + JSON_QUERY 组合:
- 先用
JSON_QUERY(json_col, '$.tags')提取当前数组(返回合法 JSON 字符串,不会被转义) - 用
CONCAT拼上新元素:CONCAT(tags_json, ',"sql"]'),注意补开头的[和逗号 - 再用
JSON_MODIFY(..., '$.tags', JSON_QUERY(new_array_str))写回——必须包一层JSON_QUERY,否则 SQL Server 会把字符串当普通文本而非 JSON 值处理
UPDATE 中多个 JSON_MODIFY 调用怎么避免重复解析开销?
一次更新要改 $.status 和 $.updated_at 两个字段,如果写两遍 JSON_MODIFY(JSON_MODIFY(...)),SQL Server 会对同一列 JSON 解析两次,性能明显下降,尤其在大 JSON 或高频更新场景。
推荐链式调用,且复用中间结果:
UPDATE t
SET json_col = JSON_MODIFY(
JSON_MODIFY(
json_col,
'$.status', 'active'
),
'$.updated_at', FORMAT(GETDATE(), 'yyyy-MM-ddTHH:mm:ss')
)
WHERE id = 123;
注意点:
- 嵌套调用顺序无关对错,但建议按业务逻辑顺序写,便于后续维护
- 不要在
JSON_MODIFY外层再套TRY_CONVERT(NVARCHAR, ...)——它已是字符串,强制转换反而可能触发隐式类型推断失败 - 如果某次修改依赖另一次的结果(比如先加字段再改值),必须严格保证嵌套顺序
JSON_MODIFY 在 SQL Server 2016+ 的兼容性陷阱
JSON_MODIFY 从 SQL Server 2016 开始支持,但早期版本(如 2016 RTM)有严重 bug:当原 JSON 含 Unicode 转义(如 "\u4f60")或尾部空白时,JSON_MODIFY 可能破坏编码或删掉空格,导致应用层解析失败。
验证方式:
- 执行
SELECT SERVERPROPERTY('ProductVersion'),确认不低于13.0.5026.0(2016 SP1) - 测试语句:
SELECT JSON_MODIFY('{"a":"\u4f60"}', '$.a', 'x'),结果应为{"a":"x"};若返回{"a":"\u4F60"}(即原字符残留),说明底层 JSON 引擎未正确清理 - 生产环境务必打到最新 CU 补丁,微软在 2017 CU15+ 才彻底修复多处 JSON 内存拷贝缺陷
真正麻烦的不是语法写不对,而是你改了十行数据,其中一行 JSON 里混着 BOM 或控制字符,JSON_MODIFY 安静地把它吃掉了,下游服务反序列化时报错却查不到源头。


















