【问题标题】:How to generate hierarchical JSON data with Microsoft SQL Server 2016?如何使用 Microsoft SQL Server 2016 生成分层 JSON 数据?
【发布时间】:2016-07-11 03:17:09
【问题描述】:

我正在使用 Microsoft SQL Server 2016。此版本支持 JSON。

我有一个 Person 表,其中包含以下数据:

PersonId FatherId Name
1 NULL 4th Grand Father
2 1 3rd Grand Father
3 2 2nd Grand Father
4 3 Grand Father
5 4 Father
6 4 Uncle
7 6 Cousin
8 5 Brother
9 5 Me

我运行以下查询:

WITH Persons_CTE AS(
    SELECT PersonId, FatherId, Name FROM Persons WHERE FatherId IS NULL
    UNION ALL
    SELECT P.PersonId, P.FatherId, P.Name FROM Persons P JOIN Persons_CTE PCTE
    ON PCTE.PersonId = P.FatherId)

    SELECT P.Name as Name, PCTE.Name as Children FROM Persons_CTE PCTE LEFT JOIN Persons P
    ON P.PersonId = PCTE.FatherId 
    FOR JSON PATH

查询生成以下结果:

[
   {
      "Children":"4th Grand Father"
   },
   {
      "Name":"4th Grand Father",
      "Children":"3rd Grand Father"
   },
   {
      "Name":"3rd Grand Father",
      "Children":"2nd Grand Father"
   },
   {
      "Name":"2nd Grand Father",
      "Children":"Grand Father"
   },
   {
      "Name":"Grand Father",
      "Children":"Father"
   },
   {
      "Name":"Grand Father",
      "Children":"Uncle"
   },
   {
      "Name":"Uncle",
      "Children":"Cousin"
   },
   {
      "Name":"Father",
      "Children":"Brother"
   },
   {
      "Name":"Father",
      "Children":"Me"
   }
]

我希望查询结果为以下分层格式。我该怎么做?

[
   {
      "Name":"4th Grand Father",
      "Children":[
         {
            "Name":"3rd Grand Father",
            "Children":[
               {
                  "Name":"2nd Grand Father",
                  "Children":[
                     {
                        "Name":"Grand Father",
                        "Children":[
                           {
                              "Name":"Father",
                              "children":[
                                 {
                                    "Name":"Brother"
                                 },
                                 {
                                    "Name":"Me"
                                 }
                              ]
                           },
                           {
                              "Name":"Uncle",
                              "children":[
                                 {
                                    "Name":"Cousin"
                                 }
                              ]
                           }
                        ]
                     }
                  ]
               }
            ]
         }
      ]
   }
]

【问题讨论】:

    标签: sql-server json


    【解决方案1】:

    当然 使用递归 CTE 实现 Json 树将非常困难(如果不是不可能的话)。

    但是,使用递归函数确实很有帮助并且解决了问题:

    ALTER  FUNCTION fn_Json(@PersonId INT, @IsRoot INT ) 
    RETURNS VARCHAR(MAX)
    BEGIN 
        DECLARE @Json NVARCHAR(MAX) = '{}', @Name NVARCHAR(MAX) , @Children                 NVARCHAR(MAX)
    
        SET @Json =  
        (SELECT P.Name ,JSON_QUERY(dbo.fn_Json(P.PersonId, 2) ) AS Children 
        FROM dbo.Persons AS P  
        WHERE P.FatherId = @PersonId 
        FOR JSON AUTO);
    
        IF(@IsRoot = 1) 
        BEGIN
           SELECT @Name = P.Name FROM dbo.Persons AS P WHERE P.PersonId = @PersonId
           SET @Json =   '{"Name":"' + @Name + '","Children":' + CAST(@Json AS NVARCHAR(MAX)) + '}'
           SET @IsRoot = 2
        END
    
        RETURN @Json 
    END 
    

    值得一提的是,如果其内部对象无效,则无法构建函数。因此,有必要将函数构建为:

    CREATE FUNCTION fn_Json(@PersonId INT, @IsRoot INT) 
    RETURNS VARCHAR(MAX)
    BEGIN 
       RETURN 1
    END 
    

    然后使用拳头代码。如果您希望包含根节点,请设置

    @IsRoot = 1
    

    如果不是@IsRoot = 2 或其他一些值

    【讨论】:

    • Afshin Amiri:请原谅我的无知,但您将如何在示例中调用该函数?我有类似的需求,我正在尝试使用您的功能,但不适合我。我确定我做错了什么
    • 如果您使用的是 SQL Server,只需使用“select fn_Json(@PersonId INT, @IsRoot INT)”并替换参数
    【解决方案2】:

    不幸的是,递归 CTe 不能用于生成分层 json 。递归 CTE 的输出仍然是平坦的结果。

    创建分层输出的唯一方法是为每个级别创建单独的 CTE,然后使用 FOR JSON AUTO 加入

    准备表格:

    declare @t table (PersonId int, FatherId int, Name nvarchar(20));
    
    insert into @t(PersonId, FatherId, Name)
    values
    (1, NULL, '4th Grand Father'),
    (2, 1, '3rd Grand Father'),
    (3, 2, '2nd Grand Father'),
    (4, 3, 'Grand Father'),
    (5, 4, 'Father'),
    (6, 4, 'Uncle'),
    (7, 6, 'Cousin'),
    (8, 5, 'Brother'),
    (9, 5, 'Me');
    

    -- 分层查询:

    WITH
    Persons_CTE1 AS(
        SELECT PersonId, FatherId, Name FROM @t WHERE FatherId IS NULL
    ),
    Persons_CTE2 AS(
        SELECT P.PersonId, P.FatherId, P.Name
        from @t P
        WHERE P.FatherId IN (SELECT PersonId FROM Persons_CTE1)
    ),
    Persons_CTE3 AS(
        SELECT P.PersonId, P.FatherId, P.Name
        from @t P
        WHERE P.FatherId IN (SELECT PersonId FROM Persons_CTE2)
    ),
    Persons_CTE4 AS(
        SELECT P.PersonId, P.FatherId, P.Name
        from @t P
        WHERE P.FatherId IN (SELECT PersonId FROM Persons_CTE3)
    ),
    Persons_CTE5 AS(
        SELECT P.PersonId, P.FatherId, P.Name
        from @t P
        WHERE P.FatherId IN (SELECT PersonId FROM Persons_CTE4)
    ),
    Persons_CTE6 AS(
        SELECT P.PersonId, P.FatherId, P.Name
        from @t P
        WHERE P.FatherId IN (SELECT PersonId FROM Persons_CTE5)
    )
    select Persons_CTE1.Name, Persons_CTE2.Name, Persons_CTE3.Name,
           Persons_CTE4.Name, Persons_CTE5.Name, Persons_CTE6.Name
    from Persons_CTE1
        LEFT JOIN Persons_CTE2
            ON Persons_CTE2.FatherId = Persons_CTE1.PersonId
        LEFT JOIN Persons_CTE3
            ON Persons_CTE3.FatherId = Persons_CTE2.PersonId
        LEFT JOIN Persons_CTE4
            ON Persons_CTE4.FatherId = Persons_CTE3.PersonId
        LEFT JOIN Persons_CTE5
            ON Persons_CTE5.FatherId = Persons_CTE4.PersonId
        LEFT JOIN Persons_CTE6
            ON Persons_CTE6.FatherId = Persons_CTE5.PersonId
    FOR JSON AUTO
    

    【讨论】:

    • 没有办法为每个级别动态地做一个CTE?您必须在查询中期望 6 个级别,对吗?如果有 7 个级别会怎样?
    猜你喜欢
    • 2015-04-13
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多