Sql server 获取祖先的有效方法-物化路径

Sql server 获取祖先的有效方法-物化路径,sql-server,materialized-path-pattern,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/ 我将节点列作为主键(集群) 现在,如果我想找到f3的祖先,给定路径,我会这样做: SELECT * FROM Files WHERE '/f1/f2/f3/' LIK

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

Table:Files

node  parentNode  name    path
 100    NULL       f1     /f1/
 101    100        f2     /f1/f2/
 102    101        f3     /f1/f2/f3/
我将
节点
列作为主键(集群)

现在,如果我想找到f3的祖先,给定路径,我会这样做:

SELECT * FROM Files WHERE '/f1/f2/f3/' LIKE [path] + '%'
问题是,执行计划使用聚集索引扫描(我认为SQL server默认用于表扫描)


在给定路径的情况下,我是否可以更有效地找到节点的祖先,最好不要使用CTE?如果需要,我还有一个
depth
栏供我使用 范围键表示X和Y之间的所有权。范围键在处理大型层次结构(180K节点)时特别有用

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

示例Hier构建

--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 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 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 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 The Full Hier
Select * 
 From #MyHier
 Order By 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
返回

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

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

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

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

示例Hier构建

--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 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 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 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 The Full Hier
Select * 
 From #MyHier
 Order By 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
返回

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

执行计划在哪里?我不确定你想要什么。因为如果path是一个字段,那么您已经有了
f3
的aster,如果您想要一个路径,那么为什么您的查询使用
'/f1/f2/f3/'
底线显示数据库模式、示例数据和预期输出。请阅读,这里是一个学习如何提高问题质量和获得更好答案的好地方。执行计划在哪里?我不确定你想要什么。因为如果path是一个字段,那么您已经有了
f3
的aster,如果您想要一个路径,那么为什么您的查询使用
'/f1/f2/f3/'
底线显示数据库模式、示例数据和预期输出。请阅读,这里是学习如何提高问题质量和获得更好答案的好地方。