我正在尝试将函数重写为内联表值函数。 数据库是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
锚点部分中的值
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;