如何使用Microsoft SQL Server 2016生成分层JSON数据?

时间:2016-07-11 03:17:09

标签: sql-server json

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

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

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"
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]

2 个答案:

答案 0 :(得分:3)

当然使用递归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 

GO

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

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

然后使用第一个代码。如果您希望包含根节点

@IsRoot = 1

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

答案 1 :(得分:1)

不幸的是,递归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