在 T-SQL 中,解析 JSON 并以动态方式处理数组似乎很清楚,需要使用 OPENJSON 表值函数来处理不知道可用索引的事实。如果这个数组是一个简单的值数组:
SELECT *
FROM OPENJSON(N'["element1","element2","element3"]')
正如人们所期望的那样,它每行呈现一个元素;非常适合计算行数和迭代值。但是,我需要反向执行此操作并构建一个值数组。为了测试我认为这样的东西可能有效:
SELECT RTRIM([value])
FROM OPENJSON(N'["element1","element2","element3"]')
ORDER BY [key] FOR JSON PATH
但我收到错误:
Msg 13605, Level 16, State 1, Line 8 Column expressions and data sources without names or aliases cannot be formatted as JSON text using FOR JSON clause. Add alias to the unnamed column or table.
我尝试过各种形式的 OPENJSON 函数和 JSON_QUERY 函数,但没有成功。最终我确实得出了这个“解决方案”:
SELECT REPLACE(REPLACE(REPLACE(CATEND,'},{"V":',','),'[{"V":','['),'}]',']')
FROM (
SELECT (
SELECT [value] AS V
FROM OPENJSON(N'["element1","element2","element3"]')
ORDER BY [key] FOR JSON PATH
) AS CATEND
) T
但这对我来说不太合适。我使用 JSON 库函数的重点和目的是避免文本解析,并且必须包含昂贵的 REPLACE 函数,感觉好像没有必要。我错过了什么吗?!
最佳答案
遗憾的是,开发人员忘记添加诸如 AS ARRAY
提示之类的内容。
OPENJSON()
可以读取裸数组,其中 [key]
是元素的位置,[value]
是项目,但我们无法使用 JSON 功能创建这样的数组:
有一些解决方法:
--创建一个模型来模拟您的问题:
DECLARE @tbl1 TABLE(ID INT IDENTITY,SomeValue VARCHAR(100));
INSERT INTO @tbl1 VALUES('Row 1'),('Row 2');
DECLARE @tbl2 TABLE(ID1 INT,SomeDetail VARCHAR(100));
INSERT INTO @tbl2 VALUES(1,'Det 1.1'),(1,'Det 1.2')
,(2,'Det 2.1'),(2,'Det 2.2.'),(2,'Det 2.3');
--使用AUTO
模式将检测JOIN
并创建对象数组。
--这很接近,但你不会得到未命名的数组:
SELECT *
FROM @tbl1 t1
INNER JOIN @tbl2 t2 ON t1.ID=t2.ID1
FOR JSON AUTO;
/*
{
"ID": 1,
"SomeValue": "Row 1",
"t2": [
{
"ID1": 1,
"SomeDetail": "Det 1.1"
},
{
"ID1": 1,
"SomeDetail": "Det 1.2"
}
]
}
*/
--使用 PATH
模式将返回每个组合 - 不是您需要的
SELECT *
FROM @tbl1 t1
INNER JOIN @tbl2 t2 ON t1.ID=t2.ID1
FOR JSON PATH;
/*
{
"ID": 1,
"SomeValue": "Row 1",
"ID1": 1,
"SomeDetail": "Det 1.1"
}
*/
--使用相关子查询将得到与上面AUTO
模式相同的结果
SELECT *
,(
SELECT *
FROM @tbl2 t2
WHERE t2.ID1=t1.ID --<-- correlated sub-query
FOR JSON PATH
) AS Details
FROM @tbl1 t1
FOR JSON PATH;
--因此我们可以将相关子查询与用于字符串连接的 XML 黑客一起使用:
SELECT *
,JSON_QUERY
(
CONCAT('['
,STUFF((
SELECT CONCAT(',"',t2.SomeDetail,'"')
FROM @tbl2 t2
WHERE t2.ID1=t1.ID --<-- correlated sub-query
FOR XML PATH(''),TYPE).value('.','nvarchar(max)'),1,1,''),']')
) AS Details
FROM @tbl1 t1
FOR JSON PATH;
--最后这就是你想要的:
/*
{
"ID": 1,
"SomeValue": "Row 1",
"Details": [
"Det 1.1",
"Det 1.2"
]
}
*/
如果您有 v2017+,那么您很幸运,因为您可以使用 STRING_AGG()
:
SELECT t1.ID
,JSON_QUERY(CONCAT('[',STRING_AGG(CONCAT('"',t2.SomeDetail,'"'),','),']')) AS Details
FROM @tbl1 t1
INNER JOIN @tbl2 t2 ON t1.ID=t2.ID1
GROUP BY t1.ID
FOR JSON PATH;
目前我们必须等待 future 的版本来原生地引入这个真正需要的功能......
关于arrays - T-SQL 解析包含动态值数组的 JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58779825/