Reputation: 21396
I have a table of items that contains all records of a multi-level hierarchy. So, there is a top level item and it has items below it which in turn has items below it and so on; also, there will be many top level items in this table.
My goal is to return records from this table such that each parent is followed by records of its children ( these children can be in any order) and each of the children is then followed by its children ( these children can be in any order) just after it and so on. I have created a SQLFiddle for this at: SQLFiddle for this
I have tried to obtain this using following query, but the third level hierarchy items are not appearing just after their parent.
select Id, ParentId, ChildLevel,
CASE WHEN ChildLevel = 0 THEN Id
WHEN ChildLevel = 1 then Id
WHEN ChildLevel > 1 THEN ParentId END Num
from Items order by UltimateParentId, ChildLevel, Num ;
Question
Is there some way of getting children appear just after its parent in above sql fiddle?
Script for creating this table with data is as below.
create table Items ( Id int , ParentId int , ChildLevel int, UltimateParentId int);
--I could have multiple hierarchy level for each top level item
--right now I am populating data at multiple levels for only one top level item
--but the table will consist of many top level items having their own hierarchy records
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (1,0,0,0);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (2,1,1,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (3,1,1,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (4,1,1,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (5,3,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (7,2,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (6,3,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (8,4,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (9,3,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (10,2,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (11,4,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (12,11,3,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (13,5,3,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (14,11,3,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (15,5,3,1);
Upvotes: 1
Views: 326
Reputation: 81930
As Requested. The following can generate a 200K hier in just a couple of seconds
I should add... Where you see
Row_Number() over (Order By ID)
I used ID, but this can be any other field to determine the desired presentation sequence.
Example
Select *,Lvl=1,Seq=cast(1000000+Row_Number() over (Order By ID) as varchar(900)) Into #TempBld from Items Where ParentID = 0
Declare @Cnt int=1
While @Cnt<=30
Begin
Insert Into #TempBld
Select A.*,Lvl=B.Lvl+1,Seq=B.Seq+' '+ cast(1000000+Row_Number() over (Order By A.ID) as varchar(25))
From Items A
Join #TempBld B on (B.Lvl=@Cnt and A.ParentID=B.ID)
Set @Cnt=@Cnt+1
End
Select ID,R1=cast(Row_Number() over (Order by Seq) as int),Seq Into #TempR1 From #TempBld
Create Clustered Index idx on #TempR1 (Seq)
Select A.ID,R2 = Max(B.R1) Into #TempR2 From #TempBld A Join #TempR1 B on B.Seq Like A.Seq+'%' Group By A.ID
Select B.R1
,C.R2
,A.Lvl
,A.ID
,A.ParentID
From #TempBld A
Join #TempR1 B on (A.ID=B.ID)
Join #TempR2 C on (A.ID=C.ID)
Order By 1
Returns
Upvotes: 1
Reputation: 81930
Since you are not using strings for the ID/Parent, you can leverage the hierarchyid
data type.
The R1,R2 and HierPath are optional in the final select. The R1 and R2 indicate ownership ranges. This facilitates aggregation and selection wihout having to go recursive.
Example
;with cteP as (
Select ID
,ParentId
,HierID = convert(hierarchyid,concat('/',ID,'/'))
From Items
Where ParentId=0
Union All
Select ID = r.ID
,ParentId = r.ParentId
,HierID = convert(hierarchyid,concat(p.HierID.ToString(),r.ID,'/'))
From Items r
Join cteP p on r.ParentId = p.ID)
Select R1 = Row_Number() over (Order By HierID)
,R2 = Row_Number() over (Order By HierID) + -1 + (Select count(*) From cteP where HierID.ToString() like A.HierID.ToString()+'%')
,Lvl = HierID.GetLevel()
,ID
,ParentId
,HierID
,HierPath = HierID.GetDescendant ( null , null ).ToString()
From cteP A
Order By A.HierID
Returns
Upvotes: 1
Reputation: 165
Yes. Common Table Expresion (CTE) can be recursive. If You use CTE You need only parrent ID. Check out https://technet.microsoft.com/pl-pl/library/ms186243(v=sql.105).aspx
Upvotes: 0