How to generate hierarchical JSON data with Microsoft SQL Server 2016?

后端 未结 2 615
难免孤独
难免孤独 2021-01-04 21:20

I\'m using Microsoft SQL Server 2016. This version supports JSON.

I have a Person table with the following data:

Pe         


        
相关标签:
2条回答
  • 2021-01-04 22:03

    Unfortunately, recursive CTe cannot be used to generate hierarchical json . Output of recursive CTE is still flat result.

    The only way to create hierarchical output is to create separate CTE for each level and then join the using FOR JSON AUTO

    Prepare table:

    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');
    

    -- Hierarchical query:

    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
    
    0 讨论(0)
  • 2021-01-04 22:09

    Sure It would be very hard (if not impossible) to implement a Json tree using recursive CTE.

    However, using recursive function is really helpful and solves the Problem:

    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

    It worth mentioning that functions can not be built if its interior objects are invalid. Therefore, it is necessary to build the function as:

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

    and then to use the fist code. If you want that the root node is included set

    @IsRoot = 1
    

    if not @IsRoot = 2 or some other values

    0 讨论(0)
提交回复
热议问题