问题
I have a table that stores a binary tree as follows:
Id ParentId Level Placement
47 -1 0 0
23 47 1 0
86 47 1 1
5 23 2 0
29 23 2 1
68 86 2 0
8 5 3 1
31 29 3 1
67 68 3 0
.
.
.
Using MSSQL now, I need to get BottomLeft of a given id, in this example, for 47 its bottom left is 5 and I need to get the bottom right of a given id, in this example for 47 its bottom right is 86
Bottom left or right is not the lowest level left or right, but extreme outside left or right leaf.
How do I write an sql that gives me a bottom left or right for a given Id?
Above placement of 0 is left and 1 is right
回答1:
You should use recursive query
here is example for leftmost query, you can easy change it to rightmost query by changing the placement to 1
with leftmost(ParentId,Id,depth) as (
select t.Id,t.Id,0
from #BinaryTree t
union all
select lm.parentid,t.Id,lm.depth+1
from leftmost lm
join #BinaryTree t on lm.Id = t.ParentId
where t.Placement = 0 -- change to 1 for right most
)
select top 1 Id
from leftmost
where parentid = 47 --replace with the id you query
order by depth desc
回答2:
Note: The final where is commented out to illustrate the full hierarchy.
Create Demonstrative Table
Declare @YourTable table (Id int,Pt int, Level int, Placement int)
Insert Into @YourTable values
(47,-1, 0,0),
(23,47, 1,0),
(86,47, 1,1),
(5 ,23, 2,0),
(29,23, 2,1),
(68,86, 2,0),
(8 , 5, 3,1),
(31,29, 3,1),
(67,68, 3,0)
The SQL -
Declare @Top int = null --<< Sets top of Hier Try 5
Declare @MaxLvl int = 99
Declare @Nest varchar(25) = ' ' --<< Optional: Added for readability
;with cteHB (Seq,ID,Pt,Lvl,Title,Placement) as (
Select Seq = cast(1000+Row_Number() over (Order by ID) as varchar(500))
,ID
,Pt
,Lvl=1
,Title =concat('Item ',ID)
,Placement
From @YourTable Where (Pt=-1 and isnull(@Top,-1) =-1) or (ID=@Top and isnull(@Top,0) <>0)
Union All
Select Seq = cast(concat(cteHB.Seq,'.',1000+Row_Number() over (Order by cteCD.ID)) as varchar(500))
,cteCD.ID
,cteCD.Pt
,cteHB.Lvl+1
,Title = concat('Item ',cteCD.ID)
,cteCD.Placement
From @YourTable cteCD
Join cteHB on cteCD.Pt = cteHB.ID and cteHB.Lvl+1<=@MaxLvl and cteCD.Placement=0)
,cteR1 as (Select Seq,ID,R1=Row_Number() over (Order By Seq) From cteHB)
,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select B.R1
,C.R2
,A.ID
,A.Pt
,A.Lvl
,Title = Replicate(@Nest,A.Lvl) + A.Title
,A.Placement
From cteHB A
Join cteR1 B on A.ID=B.ID
Join cteR2 C on A.ID=C.ID
--Where R1=R2
Order By B.R1
Returns
R1 R2 ID Pt Lvl Title Placement
1 9 47 -1 1 Item 47 0
2 6 23 47 2 Item 23 0
3 4 5 23 3 Item 5 0
4 4 8 5 4 Item 8 1
5 6 29 23 3 Item 29 1
6 6 31 29 4 Item 31 1
7 9 86 47 2 Item 86 1
8 9 68 86 3 Item 68 0
9 9 67 68 4 Item 67 0
来源:https://stackoverflow.com/questions/39251294/binary-tree-get-bottom-extreme-left-or-right