2017-01-24 117 views
0

的母子關係我有一個包含名稱的列表,地圖回父字段的表中的項目如下表 -SQL Sever的父母,子女,表

id nameVal parentId 
1 A NULL 
2 B NULL 
3 C NULL 
4 D NULL 
5 E NULL 
6 A1 1 
7 A2 6 
8 A3 1 
9 A4 7 
10 B1 2 
11 B2 2 

它可以不止一個步驟從父記錄了 - A,A1,A4都與等下文......

A1 => A 
A2 => A1 => A 
A3 => A 
A4 => A2 => A1 => A 

那麼我現在要做的是通過地方關係存在的所有記錄拉,即A4由於存在與原始A記錄的鏈接,將帶回所有的A。

這可能嗎?

+1

看看公用表表達式(CTE)。如果你想在同一個查詢中同時使用父和子關係(但是可行),那麼這麼做是非常重要的。請參閱http://stackoverflow.com/q/4740748/67392。 – Richard

回答

3

您可以使用遞歸查詢來獲取所有相關行。數據是否會以你想要的形式出現,我不知道,因爲這個問題似乎有點不清楚。但對於例如

取的記錄,所有後代:

with r as (
    select * 
     from my_table t 
     where id = 7 
    union all 
    select t1.* 
     from   my_table t1 
      inner join r 
        on t1.parent_id = r.id 
) 
select * from r; 

取的記錄,所有祖先:

with r as (
    select * 
     from my_table t 
     where id = 7 
    union all 
    select t1.* 
     from   my_table t1 
      inner join r 
        on t1.id = r.parent_id 
) 
select * from r; 

現在,也許你想兩個孩子和祖先。這可能會有點棘手;遞歸在一條直線上效果最好,因此不會出現無限循環。一種方法是將上述兩個查詢結合在一起。如果你真正的查詢有複雜的邏輯,你不想寫兩遍,那麼你可以用它來獲得一個ID列表,然後通過select ... where id in (my_list)類型的查詢運行真正的查詢。

另一個考慮因素是記錄是否可以有多個孩子。如果我們有

A 
A1 => A 
A10 => A1 
A11 => A1 
A2 => A 
A20 => A2 
A21 => A2 

你可以說這些都是相關的(通過A;有些是「堂兄弟」)。因此,如果您從A1進行搜索並將前兩個示例查詢結合起來,則會得到A, A1, A10, A11 ...但您是否還希望A的其他子項?如果是這樣,你可以採取稍微不同的方式:

首先,找到最年長的祖先:

with r as (
    select * 
     from my_table t 
     where id = 7 
    union all 
    select t1.* 
     from   my_table t1 
      inner join r 
        on t1.id = r.parent_id 
) 
select id from r where parent_id is null; 

然後運行鍼對該ID的原創「的所有後代」查詢。如果你想獲得所有到單個語句,下面的*應*工作,我認爲(但我不是,我可以測試它):

with ancestors as (
    select * 
     from my_table t 
     where id = 7 
    union all 
    select t1.* 
     from   my_table t1 
      inner join ancestors 
        on t1.id = ancestors.parent_id 
) , related as (
    select * 
     from ancestors 
     where parent_id is null 
    union all 
    select t1.* 
     from   my_table t1 
      inner join related 
        on t1.parent_id = related.id 
) 
select * from related; 
+0

感謝馬克 - 如果我只是想獲得孩子的記錄,這很有效。將ID傳遞爲1將返回所有子記錄,但如果我將該ID作爲7傳遞,它將僅返回記錄7和9,因此對於不清晰的道歉 - 我想返回父母記錄以及提供id = 7它將返回記錄1,6,7,8,9 – Spufferoo

+0

將更新更詳細的例子 –

+0

輝煌的感謝馬克 - 這已完美工作 – Spufferoo

3

你可以用遞歸CTE來做到這一點。關鍵是要獲得父母。以下是一種方法:

with cte as (
     select id, nameval, id as orig 
     from t 
     where parentid is null 
     union all 
     select t.nameval, cte.orig 
     from cte join 
      t 
      on t.parentid = cte.id 
    ) 
select cte.* 
from cte 
where cte.orig = (select cte2.orig from cte cte2 where ct2.nameval = 'A4'); 
3

也許比需要多一點,但考慮以下:

您可以設置頂級節點(空默認爲整個層次結構)

您還可以設置過濾器。對於沒有過濾器,單個ID或ID的分隔字符串,這可以爲空。

Declare @T table (id int,nameVal varchar(50),parentId int) 
Insert into @T values 
(1 ,'A', NULL), 
(2 ,'B', NULL), 
(3 ,'C', NULL), 
(4 ,'D', NULL), 
(5 ,'E', NULL), 
(6 ,'A1', 1), 
(7 ,'A2', 6), 
(8 ,'A3', 1), 
(9 ,'A4', 7), 
(10 ,'B1', 2), 
(11 ,'B2', 2) 

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

;with cteP as (
     Select Seq = cast(10000+Row_Number() over (Order by nameVal) as varchar(500)) 
      ,ID 
      ,parentId 
      ,Lvl=1 
      ,nameVal 
     From @T 
     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.nameVal)) as varchar(500)) 
      ,r.ID 
      ,r.parentId 
      ,p.Lvl+1 
      ,r.nameVal 
     From @T 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.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) 
    ,cte as (
       Select A.R1 
         ,B.R2 
         ,A.ID 
         ,A.parentId 
         ,A.Lvl 
         ,nameVal = Replicate(@Nest,A.Lvl-1) + A.nameVal 
       From cteR1 A 
       Join cteR2 B on A.ID=B.ID 
       ) 
Select Distinct A.* 
From cte A 
Join (
     Select A.R1,A.R2 
     From cte A 
     Join (Select R1 from cte Where IIF(@Filter='',1,0)+CharIndex(concat(',',ID,','),concat(',',@Filter+','))>0) B 
      on B.R1 between A.R1 and A.R2 
    ) B on A.R1 between B.R1 and B.R2 
Order By A.R1 

返回

enter image description here

現在,如果你設置@Filter = '7,10',你會得到

enter image description here

如果設置@Filter = '',你會得到

enter image description here