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_AGGSTRING_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 字符串的主要内容,如果未能解决你的问题,请参考以下文章

一个二叉树能有几个根结点?

从具有特定根的 SQL 表中获取最新分支的最有效方法是啥?

具有不同根的 Django URL 模式

DZY的根(思维水)

树与二叉树

在Sql Server 2016中使用For Json子句把数据作为json格式导出