SQL 使用具有 2 个根的 FOR JSON PATH 生成 JSON 字符串
Posted
技术标签:
【中文标题】SQL 使用具有 2 个根的 FOR JSON PATH 生成 JSON 字符串【英文标题】:SQL Generate JSON string using FOR JSON PATH with 2 ROOTs 【发布时间】:2021-12-30 20:17:13 【问题描述】:我有一个包含 5 列的非常简单的表,该表一次只能保存 1 条记录。我要从记录中生成一个 JSON 字符串并将其发送到端点。
这就是 JSON 字符串的格式。如您所见,它包含 2 个“根”,这让我很难获得正确的格式
"fields": [
"fieldName": "Brand",
"values": [
"FORD"
]
,
"fieldName": "Engine",
"values": [
"V12"
]
,
"fieldName": "Location",
"values": [
"Monaco"
]
],
"categories": [
"fieldName": "Colour",
"values": [
[
"name": "Blue"
]
]
,
"fieldName": "Interior",
"values": [
[
"name": "Red"
]
]
]
这是我的包含 5 列的表格
我设法创建了 2 个单独的 SQL 查询来获取 JSON 字符串。但是我无法弄清楚如何在一次选择中做到这一点。
SELECT (
SELECT X.* FROM (
SELECT CASE WHEN CarName IS NOT NULL THEN 'Brand' ELSE NULL END AS fieldName,
CarName AS [value]
FROM [dbo].[JSONBODY]
UNION
SELECT CASE WHEN Engine IS NOT NULL THEN 'Engine' ELSE NULL END AS fieldName,
Engine AS [value]
FROM [dbo].[JSONBODY]
UNION
SELECT CASE WHEN [location] IS NOT NULL THEN 'Location' ELSE NULL END AS fieldName,
[Location] AS [value]
FROM [dbo].[JSONBODY] ) X
FOR JSON PATH, ROOT('fields'))
SELECT (
SELECT Y.* FROM (
SELECT CASE WHEN Colour IS NOT NULL THEN 'Colour' ELSE NULL END AS fieldName,
JSON_QUERY('[["' + Colour + '"]]') AS 'value.name'
FROM [dbo].[JSONBODY]
UNION
SELECT CASE WHEN Interior IS NOT NULL THEN 'Interior' ELSE NULL END AS fieldName,
JSON_QUERY('[["' + Interior + '"]]') AS 'value.name'
FROM [dbo].[JSONBODY]) Y
FOR JSON PATH, ROOT('categories'))
这里有 2 个 JSON 字符串:
"fields":["fieldName":"Brand","value":"Ford","fieldName":"Engine","value":"V6","fieldName":"Location","value":"Boston"]
"categories":["fieldName":"Colour","value":"name":[["Blue"]],"fieldName":"Interior","value":"name":[["Black"]]]
问题 1: 是否可以通过单个 SQL Select 创建 JSON 字符串?我该怎么做?
问题 2:
如果列值为 NULL,它会自动从 JSON 字符串中排除。但我不得不将fieldName
添加到选择中,并希望如果相应的字段为NULL,它将从JSON 字符串中排除它。但是,它会在 JSON 字符串中创建一个 。这在调用端点时不被接受。那么当列值为NULL时还有另一种方法吗?之后我当然可以从 JSON 字符串中删除它....
希望以上内容有意义
【问题讨论】:
提问时,您需要提供minimal reproducible example: (1) DDL 和样本数据填充,即 CREATE 表和 INSERT T-SQL 语句。 (2) 你需要做什么,即逻辑和你的代码尝试在 T-SQL 中实现它。 (3) 期望的输出,基于上述#1 中的样本数据。 (4) 您的 SQL Server 版本 (SELECT @@version;)。 【参考方案1】:要将其作为单个SELECT
进行,您可以将两个结果放在一起UNION ALL
您可以取消透视这些值,然后检查它们是否为空值。
不幸的是,SQL Server 没有JSON_AGG
,所以你必须使用STRING_AGG
和STRING_ESCAPE
来解决它
SELECT
v.fieldName,
value = JSON_QUERY('[' + STRING_AGG('"' + STRING_ESCAPE(v.value, 'json') + '"', ',') + ']')
FROM [dbo].[JSONBODY] jb
CROSS APPLY (VALUES
('Brand', jb.Brand),
('Engine', jb.Engine),
('Location', jb.Location)
) v(fieldName, value)
GROUP BY
v.fieldName
FOR JSON PATH, ROOT('fields');
UNION ALL
SELECT
v.fieldName,
[value.name] = JSON_QUERY('[[' + STRING_AGG('"' + STRING_ESCAPE(v.value, 'json') + '"', ',') + ']]')
FROM [dbo].[JSONBODY] jb
CROSS APPLY (VALUES
('Colour', jb.Colour),
('Interior', jb.Interior)
) v(fieldName, value)
GROUP BY
v.fieldName
FOR JSON PATH, ROOT('categories');
如果你知道你只会有一行,你可以通过删除GROUP BY
来简化它
SELECT (
SELECT
v.fieldName,
value = JSON_QUERY('["' + STRING_ESCAPE(v.value, 'json') + '"]')
FROM [dbo].[JSONBODY] jb
CROSS APPLY (VALUES
('Brand', jb.Brand),
('Engine', jb.Engine),
('Location', jb.Location)
) v(fieldName, value)
WHERE v.value IS NOT NULL
FOR JSON PATH, ROOT('fields')
)
UNION ALL
SELECT (
SELECT
v.fieldName,
[value.name] = JSON_QUERY('[["' + STRING_ESCAPE(v.value, 'json') + '"]]')
FROM [dbo].[JSONBODY] jb
CROSS APPLY (VALUES
('Colour', jb.Colour),
('Interior', jb.Interior)
) v(fieldName, value)
WHERE v.value IS NOT NULL
FOR JSON PATH, ROOT('categories')
);
db<>fiddle
【讨论】:
以上是关于SQL 使用具有 2 个根的 FOR JSON PATH 生成 JSON 字符串的主要内容,如果未能解决你的问题,请参考以下文章