如何用OPENJSON读取嵌套转义JSON值及SQL查询报错排查
问题分析与解决方案
表结构
Tackle表:
| TackleID | TackleName |
|---|---|
| 5e8ef3ed-d02a-4582-85d3-4a993521ed0f | Name1 |
| f08c1041-e5b8-4f25-b69b-90d6a59d937e | Name2 |
| 8a9317fe-e036-46b4-a1c2-36b5ba0191dc | Name3 |
TackleResult表:
| TackleResultID | TackleID | RDI |
|---|---|---|
| 91f50120-dd46-4f10-900d-42e2338dd649 | 5e8ef3ed-d02a-4582-85d3-4a993521ed0f | [{"Name":"SeIds","Value":"Data"},{"Name":"ISD","Value":"{"SID":{"column":{"City":"Himachal","Depth":"5mts"}},"Canded":false}"}] |
| 949fc210-7349-4dcf-9a64-08c1259cb3c6 | f08c1041-e5b8-4f25-b69b-90d6a59d937e | [{"Name":"SeIds","Value":"Data"},{"Name":"ISD","Value":"{"SID":{"column":{"City":"Delhi","Depth":"15mts"}},"Canded":false}"}] |
| a2162652-27cf-49ea-ba16-b4a396b04030 | 8a9317fe-e036-46b4-a1c2-36b5ba0191dc | [{"Name":"SeIds","Value":"Data"},{"Name":"ISD","Value":"{"SID":{"column":{"City":"Agra","Depth":"15mts"}},"Canded":false}"}] |
Main表:
| MainID | TackleID |
|---|---|
| 45e0fa71-5091-4523-87f1-4f04bdd98cd7 | 5e8ef3ed-d02a-4582-85d3-4a993521ed0f |
需求
根据标志位@showDelhi筛选数据:
- 当
@showDelhi = 1时,显示所有城市为Delhi、Himachal、Agra的记录 - 当
@showDelhi = 0时,仅显示城市为Himachal、Agra的记录,排除Delhi
原查询问题
给定的SQL查询在@showDelhi = 1时正常运行,但@showDelhi = 0时抛出错误:
JSON text is not properly formatted. Unexpected character 't' is found at position 2.
报错原因
- JSON解析路径错误:原查询嵌套
OPENJSON时,内层SELECT VALUE写法错误。使用OPENJSON(... WITH (Name nvarchar(100), Value nvarchar(max)))后,结果集列名为Name和Value,而非未指定WITH时的默认列VALUE,导致取出的不是ISD对应的有效JSON字符串,引发格式错误。 - 不必要的类型转换:将
TRD.RDI转换为VARCHAR(MAX)可能导致Unicode字符损坏,破坏JSON结构,出现意外字符。 - 逻辑写法不规范:
1 = @showDelhi的写法可读性差,且SQL优化器可能不会短路执行,浪费性能。
优化后的查询方案
方案1:用CTE提前解析JSON(高可读性)
DECLARE @MainId uniqueidentifier = '45e0fa71-5091-4523-87f1-4f04bdd98cd7' DECLARE @showDelhi bit = 0; WITH ParsedTackleResult AS ( SELECT TRD.TackleID, JSON_VALUE(TRD_RDI.Value, '$.SID.column.City') AS City FROM TackleResult TRD CROSS APPLY OPENJSON(TRD.RDI) WITH ( Name nvarchar(100) '$.Name', Value nvarchar(max) '$.Value' ) AS TRD_RDI WHERE TRD_RDI.Name = 'ISD' ) SELECT TG.TackleID FROM Main P INNER JOIN Tackle TG ON TG.TackleID = P.TackleID INNER JOIN ParsedTackleResult PTR ON PTR.TackleID = TG.TackleID WHERE P.MainId = @MainId AND ( @showDelhi = 1 OR PTR.City != 'Delhi' );
方案2:直接在查询中链式解析JSON(简洁)
DECLARE @MainId uniqueidentifier = '45e0fa71-5091-4523-87f1-4f04bdd98cd7' DECLARE @showDelhi bit = 0; SELECT TG.TackleID FROM Main P INNER JOIN Tackle TG ON TG.TackleID = P.TackleID INNER JOIN TackleResult TRD ON TRD.TackleID = TG.TackleID CROSS APPLY OPENJSON(TRD.RDI) WITH ( Name nvarchar(100) '$.Name', Value nvarchar(max) '$.Value' ) AS TRD_RDI CROSS APPLY OPENJSON(TRD_RDI.Value) WITH ( City nvarchar(100) '$.SID.column.City' ) AS CityData WHERE P.MainId = @MainId AND TRD_RDI.Name = 'ISD' AND ( @showDelhi = 1 OR CityData.City != 'Delhi' );
关键优化点
- 用
CROSS APPLY替代嵌套子查询,提升JSON解析的性能与可读性 - 明确指定
OPENJSON的映射路径,避免解析错误 - 移除不必要的
VARCHAR转换,保留NVARCHAR类型确保JSON完整性 - 使用直观的逻辑判断替代
1=@showDelhi,增强代码可维护性 - 提前过滤
Name='ISD'的JSON元素,减少无效解析操作
内容的提问来源于stack exchange,提问作者SAREKA AVINASH
相关产品推荐
相关产品推荐

