获得祖先的有效方式 - 物化路径

时间:2017-03-30 19:53:31

标签: sql-server materialized-path-pattern

我有一个使用物化路径存储的分层数据结构。

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列。

1 个答案:

答案 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

<强>返回

enter image description here

获取祖先

-- 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

<强>返回

enter image description here

选择后代

-- 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

<强>返回

enter image description here