SQL Server 递归 CTE 分解 JSON:锚点和递归部分之间的类型不匹配

问题描述 投票:0回答:1

我正在尝试将函数重写为内联表值函数。 数据库是SQL Server 2022。 请看下面的SQL Fiddle。我尝试使用显式 CAST 进行多次重写,但它仍然不适用于 OPENJSON 函数中的密钥。然而,在没有该列的情况下运行查询可以按预期工作。

您能告诉我为什么会出现此错误以及如何修复它吗?

感谢 @siggemannen 提供的解决方案:密钥的排序规则也必须更改。这里有一个 SQL Fiddle 来显示工作解决方案。

这是代码示例:

  DECLARE @json NVARCHAR(MAX) =N'{"menu": {  
  "id": "file",  
  "value": "File",  
  "popup": {  
    "menuitem": [  
      {"value": "New", "onclick": "CreateDoc()"},  
      {"value": "Open", "onclick": "OpenDoc()"},  
      {"value": "Save", "onclick": "SaveDoc()"}  
    ]  
  }  
}}  '
;
/* code adapted from Phil Factor 
https://www.red-gate.com/simple-talk/databases/sql-server/t-sql-programming-sql-server/importing-json-web-services-applications-sql-server/
*/
WITH cteRecurseJSON AS
(
    SELECT 
        CAST(1 AS INTEGER) AS Depth, 
        --CAST('$' AS NVARCHAR(4000)) AS ThePath,
        CAST(N'' AS NVARCHAR(4000)) AS ThePath,
        CAST(@json  AS NVARCHAR(MAX)) AS TheValue, 
        CAST('object' AS VARCHAR(10)) AS ValueType
    UNION ALL
    SELECT 
        r.Depth+1 AS Depth,
            CAST(v.[Key] AS NVARCHAR(4000)) AS ThePath,
        Coalesce(Value,'') AS TheValue,
        CAST(CASE Type WHEN 1 THEN 'string'
                WHEN 0 THEN 'null'
                WHEN 2 THEN 'int'
                WHEN 3 THEN 'boolean'
                WHEN 4 THEN 'array' ELSE 'object' END AS VARCHAR(10)) AS ValueType
    FROM cteRecurseJSON r
    CROSS APPLY OPENJSON(r.TheValue) v
    WHERE r.ValueType IN ('array','object')

)
SELECT *
FROM cteRecurseJSON
json sql-server recursion common-table-expression
1个回答
0
投票

锚点部分中的值

ThePath
需要为
nvarchar(max)
并且您需要更改排序规则 ,如@siggemanen所述。

您还应该使用

STRING_ESCAPE
来正确转义路径。您只需检查数组即可简化
CASE

WITH cteRecurseJSON AS
(
    SELECT 
        CAST(1 AS INTEGER) AS Depth, 
        CAST(N'$' AS NVARCHAR(max))  COLLATE database_default AS ThePath,
        CAST(jsonValue  AS NVARCHAR(MAX)) AS TheValue, 
        CAST('object' AS VARCHAR(10)) AS ValueType
    FROM test
    UNION ALL
    SELECT 
        r.Depth+1 AS Depth,
        CAST(
          r.ThePath +
          CASE WHEN r.ValueType = 'array' THEN '[' + v.[key] + ']'
               WHEN STRING_ESCAPE(v.[key], 'json') <> v.[key] THEN '."' + STRING_ESCAPE(v.[key], 'json') + '"' --got a space in it
               ELSE '.' + v.[Key]
            END
          AS  NVARCHAR(MAX)) AS ThePath,
        Coalesce(Value,'') AS TheValue,
        CAST(CASE Type
                WHEN 1 THEN 'string'
                WHEN 0 THEN 'null'
                WHEN 2 THEN 'int'
                WHEN 3 THEN 'boolean'
                WHEN 4 THEN 'array'
                ELSE 'object' END AS VARCHAR(10)) AS ValueType
    FROM cteRecurseJSON r
    CROSS APPLY OPENJSON(r.TheValue) v
    WHERE r.ValueType IN ('array', 'object')
)
SELECT *
FROM cteRecurseJSON;

db<>小提琴

© www.soinside.com 2019 - 2024. All rights reserved.