2016-11-29 67 views
1

我需要得到一个给定某个节点的相关节点树,但不是必需的顶层节点。我已经有了一个使用两个CTE的解决方案,因为我正在努力把它全部整合到一个CTE中:)。可能有人有一个圆滑的解决方案来避免使用两个CTE?下面是一些代码,我跟打:sql递归:找到树给定中间节点

DECLARE @temp AS TABLE (ID INT, ParentID INT) 
INSERT INTO @temp 
SELECT 1 ID, NULL AS ParentID 
UNION ALL 
SELECT 2, 1 
UNION ALL 
SELECT 3, 2 
UNION ALL 
SELECT 4, 3 
UNION ALL 
SELECT 5, 4 
UNION ALL 
SELECT 6, NULL 
UNION ALL 
SELECT 7, 6 
UNION ALL 
SELECT 8, 7 

DECLARE @startNode INT = 4 
;WITH TheTree (ID,ParentID) 
AS (
    SELECT ID, ParentID 
    FROM @temp 
    WHERE ID = @startNode 
    UNION ALL 
    SELECT t.id, t.ParentID 
    FROM @temp t 
    JOIN TheTree tr ON t.ParentID = tr.ID 
    ) 
SELECT * FROM TheTree 

;WITH Up(ID,ParentID) 
AS (
    SELECT t.id, t.ParentID 
    FROM @temp t 
    WHERE t.ID = @startNode 
    UNION ALL 
    SELECT t.id, t.ParentID 
    FROM @temp t 
    JOIN Up c ON t.id = c.ParentID 
    ) 
    --SELECT * FROM Up 
,TheTree (ID,ParentID) 
AS (
    SELECT ID, ParentID 
    FROM Up 
    WHERE ParentID is null 
    UNION ALL 
    SELECT t.id, t.ParentID 
    FROM @temp t 
    JOIN TheTree tr ON t.ParentID = tr.ID 
    ) 
SELECT * FROM TheTree 

感谢

回答

1

咩。这样可以避免使用两个CTE,但是结果是一个难以称为“圆滑”的蛮力kludge,因为如果您的表格大小合适,它将不会有效。它将:

  • 递归构建所有可能的层次
  • 在构建他们,标志目标的NodeId为你找到它
  • 只返回目标树

我在列“TreeNumber扔“TargetId出现在多个层次结构中的机会不足,或者如果您有多个值来检查一次传递。 “深度”被添加使输出更清晰一点。

像@约翰的可以做,而且越来越微妙的技巧更复杂的解决方案可以提供更详细的表sturctures来完成。

DECLARE @startNode INT = 4 

;WITH cteAllTrees (TreeNumber, Depth, ID, ParentID, ContainsTarget) 
AS (
    SELECT 
     row_number() over (order by ID) TreeNumber 
     ,1 
     ,ID 
     ,ParentID 
     ,case 
     when ID = @startNode then 1 
     else 0 
     end ContainsTarget 
    FROM @temp 
    WHERE ParentId is null 
    UNION ALL 
    SELECT 
     tr.TreeNumber 
     ,tr.Depth + 1 
     ,t.id 
     ,t.ParentID 
     ,case 
     when tr.ContainsTarget = 1 then 1 
     when t.ID = @startNode then 1 
     else 0 
     end ContainsTarget 
    FROM @temp t 
    INNER JOIN cteAllTrees tr 
    ON t.ParentID = tr.ID 
    ) 
SELECT 
    TreeNumber 
    ,Depth 
    ,ID 
    ,ParentId 
from cteAllTrees 
where TreeNumber in (select TreeNumber from cteAllTrees where ContainsTarget = 1) 
order by 
    TreeNumber 
    ,Depth 
    ,ID 
+0

非常感谢。正如你所说的'几乎不符合'光滑'':),从性能角度来看,我认为最好跟2个CTE一起去。我记得教授谈到用两个锚(递回的方式)递归时,只是希望有人知道它,它适用于我的情况。 – user2065377

+0

可能会有某种方法在递归连接中嵌入'OR'子句,以同时上下移动,但我无法弄清楚如何排除先前选择的行。 –

+0

听到听到:)同样在这里... – user2065377

1

这是一种技术,您可以选择整个层次结构,其所有的孩子一个特定节点,甚至过滤列表以及它们如何滚。

注:参见注释旁边声明

Declare @YourTable table (id int,pt int,name varchar(50)) 
Insert into @YourTable values 
(1,null,'1'),(2,1,'2'),(3,1,'3'),(4,2,'4'),(5,2,'5'),(6,3,'6'),(7,null,'7'),(8,7,'8') 

Declare @Top int   = null  --<< Sets top of Hier Try 2 
Declare @Nest varchar(25) = '|-----' --<< Optional: Added for readability 
Declare @Filter varchar(25) = ''  --<< Empty for All or try 4,6 

;with cteP as (
     Select Seq = cast(1000+Row_Number() over (Order by name) as varchar(500)) 
      ,ID 
      ,pt 
      ,Lvl=1 
      ,name 
     From @YourTable 
     Where IsNull(@Top,-1) = case when @Top is null then isnull(pt,-1) else ID end 
     Union All 
     Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.name)) as varchar(500)) 
      ,r.ID 
      ,r.pt 
      ,p.Lvl+1 
      ,r.name 
     From @YourTable r 
     Join cteP p on r.pt = p.ID) 
    ,cteR1 as (Select *,R1=Row_Number() over (Order By Seq) From cteP) 
    ,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 Distinct 
     A.R1 
     ,B.R2 
     ,A.ID 
     ,A.pt 
     ,A.Lvl 
     ,name = Replicate(@Nest,A.Lvl-1) + A.name 
From cteR1 A 
Join cteR2 B on A.ID=B.ID 
Join (Select R1 From cteR1 where IIF(@Filter='',1,0)+CharIndex(concat(',',ID,','),concat(',',@Filter+','))>0) F on F.R1 between A.R1 and B.R2 
Order By A.R1 
+0

感谢,但它不完全是 '它'。我不需要建立一个完整的树。我在一张表(大约5万个记录)中有许多独立的树,其中大多数只是根树(1个节点),大约10%将有1到20个节点。我需要的只是找到属于给定节点的树的节点列表,顺便说一下可能是根节点,但也可以是尾节点或中间节点。但无论如何感谢。以上可以是有用的算法。 – user2065377