0

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

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专栏供我使用。

4

1 回答 1

2

如果您的层次结构移动缓慢,请考虑添加范围键。它们有助于导航、过滤和/或聚合,而不需要递归。

Range 键表示 X 和 Y 之间的所有权。在处理大型层次结构(180K 节点)时,范围键特别有用。

以下是一个简化的示例,但可能会有所帮助。

样本层次构建

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

退货

在此处输入图像描述

于 2017-03-30T20:58:18.883 回答