I'm using Microsoft SQL Server 2016. This version supports JSON.
I have a Person table with the following data:
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 |
I run the following query:
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
The query generates the following result:
[
{
"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"
}
]
I want the query result to the following hierarchical format. How can I do it?
[
{
"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"
}
]
}
]
}
]
}
]
}
]
}
]
Sure It would be very hard (if not impossible) to implement a Json tree using recursive CTE.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With