我有一个使用物化路径存储的分层数据结构。
Table:Files
node parentNode name path
100 NULL f1 /f1/
101 100 f2 /f1/f2/
102 101 f3 /f1/f2/f3/
我将node
列作为主键(群集)
现在如果我想找到f3
的祖先,给定路径,我会这样做:
SELECT * FROM Files WHERE '/f1/f2/f3/' LIKE [path] + '%'
问题在于,执行计划使用聚簇索引扫描(我认为SQL服务器默认用于表扫描)
无论如何,我能找到一个节点的祖先,给定路径的方式更有效,最好不使用CTE?如果需要,我还可以使用depth
列。
答案 0 :(得分:2)
如果移动层次结构缓慢,请考虑添加范围键。它们便于导航,过滤和/或聚合,无需递归。
Range键表示X和Y之间的所有权。范围键在处理大型层次结构(180K节点)时特别有用。
以下是一个简化示例,但可能有所帮助。
样本Hier Build
--Drop Table #MyHier
Declare @YourTable table (id int,ParentId int,Name varchar(50))
Insert into @YourTable values
(11, NULL,'A')
,(12, 11 ,'B')
,(13, 12 ,'F')
,(14, 13 ,'C')
,(15, 13 ,'D')
,(16, 11 ,'E')
,(17, 12 ,'G')
,(18, NULL ,'M')
,(19, 18 ,'N')
,(20, 18 ,'O')
,(21, 20 ,'P')
Declare @Top int = null --<< Sets top of Hier Try 3
Declare @Nest varchar(25) = '|-----' --<< Optional: Added for readability
;with cteP as (
Select Seq = cast(10000+Row_Number() over (Order by Name) as varchar(500))
,ID
,ParentId
,Lvl=1
,Name
,Path = cast('/'+Name+'/' as varchar(500))
From @YourTable
Where IsNull(@Top,-1) = case when @Top is null then isnull(ParentId ,-1) else ID end
Union All
Select Seq = cast(concat(p.Seq,'.',10000+Row_Number() over (Order by r.Name)) as varchar(500))
,r.ID
,r.ParentId
,p.Lvl+1
,r.Name
,cast(p.path + '/'+r.Name+'/' as varchar(500))
From @YourTable r
Join cteP p on r.ParentId = p.ID)
,cteR1 as (Select *,R1=Row_Number() over (Order By Seq) From cteP)
,cteR2 as (Select 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 A.R1
,B.R2
,A.ID
,A.ParentId
,A.Lvl
,Name = Replicate(@Nest,A.Lvl-1) + A.Name
,Path
Into #MyHier
From cteR1 A
Join cteR2 B on A.ID=B.ID
选择完整家庭
-- Get The Full Hier
Select *
From #MyHier
Order By R1
<强>返回强>
获取祖先
-- Get Ancestors of a Node
Declare @GetAncestors int = 15
Select A.*
From #MyHier A
Join (Select R1 From #MyHier Where ID=@GetAncestors) B
on B.R1 between A.R1 and A.R2
Order By A.R1
<强>返回强>
选择后代
-- Get Descendants of a Node
Declare @GetDesendants int = 12
Select A.*
From #MyHier A
Join (Select R1,R2 From #MyHier Where ID=@GetDesendants) B
on A.R1 between B.R1 and B.R2
Order By A.R1
<强>返回强>