2017-01-24 114 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