CTE用于具有多个父级的父子关系



我有一个父子关系表,如下所示。我想检索所有记录的父或子ID像所有祖先和父母,如果可能的话,与深度。例如,我想找到D的族,它将返回前14行,因为它们都属于同一族。这样的家庭可能有好几套。我想查询一个成员,想得到整个家庭的记录。是否有可能使用CTE来实现这一点?每个表记录的族结构:

                      A
                     / 
                    B   C   G   J
                   /      /  / 
              M   D       E   H   K
             /  /              / 
            N   F               I   L

                 R
                 |
                 S   U
                   /
                   T

请帮助。表是这样的:

   Parent   Child
    ------  ------
    A            B
    A            C
    B            D
    D            F
    M            F
    M            N
    C            E
    G            E
    G            H
    J            H
    J            K
    H            I
    K            I
    K            L
    R            S
    S            T
    U            T

谢谢,

Himadri

此解决方案仅适用于非循环图,因为如果循环使用所提供的数据集,递归将中断…

create table nodes
(
    p char, 
    c char
)
insert into nodes (p, c)
values
    ('A', 'B'), ('A', 'C'), ('B', 'D'), ('D', 'F'), ('M', 'F'), ('M', 'N'), ('C', 'E'),
    ('G', 'E'), ('G', 'H'), ('J', 'H'), ('J', 'K'), ('H', 'I'), ('K', 'I'), ('K', 'L'),
    ('R', 'S'), ('S', 'T'), ('U', 'T')
GO

我们可以通过对原始节点记录递归得到前向树的父->子

CREATE VIEW dbo.Tree
AS
    WITH Hierarchy(r, p, c, [Level])
    AS 
    (
        SELECT p AS r,
               p,
               c,
               0 AS [Level]
        FROM dbo.nodes
        UNION ALL
        SELECT n.p AS r,
               t.p,
               t.c,
               t.[Level] + 1
        FROM Hierarchy t
        INNER JOIN dbo.nodes n ON n.c = t.r
        AND n.p != t.p
    )
    SELECT r, p, c, [Level]
    FROM Hierarchy

然后给出结果

r   p   c   Level
A   A   B   0
A   A   C   0
A   C   E   1
A   D   F   2
A   B   D   1
B   D   F   1
B   B   D   0
C   C   E   0
D   D   F   0
G   G   E   0
G   G   H   0
G   H   I   1
H   H   I   0
J   H   I   1
J   K   L   1
J   K   I   1
J   J   H   0
J   J   K   0
K   K   I   0
K   K   L   0
M   M   F   0
M   M   N   0
R   R   S   0
R   S   T   1
S   S   T   0
U   U   T   0

我们可以用另一种方式来做,这样我们就可以从子节点向上走到父节点

CREATE VIEW dbo.ReverseTree
AS
    WITH Hierarchy(r, c, p, [Level])
    AS 
    (
        SELECT c AS r,
               c,
               p,
               0 AS [Level]
        FROM dbo.nodes
        UNION ALL
        SELECT n.c AS r,
               t.c,
               t.p,
               t.[Level] + 1
        FROM Hierarchy t
        INNER JOIN dbo.nodes n ON n.p = t.r
        AND n.c != t.c
    )
    SELECT r, c, p, [Level]
    FROM Hierarchy

结果是这样的

r   c   p   Level
B   B   A   0
C   C   A   0
D   D   B   0
D   B   A   1
E   C   A   1
E   E   C   0
E   E   G   0
F   F   D   0
F   F   M   0
F   D   B   1
F   B   A   2
H   H   G   0
H   H   J   0
I   I   H   0
I   I   K   0
I   K   J   1
I   H   J   1
I   H   G   1
K   K   J   0
L   K   J   1
L   L   K   0
N   N   M   0
S   S   R   0
T   T   S   0
T   T   U   0
T   S   R   1

这对于像面包屑路径这样的东西很方便

我找到了一个解决方案。但是我在while循环中使用了CTE。如果有人有任何其他的解决方案,请建议。正如我上面提到的一个包含家庭记录的表格,或者你可以说图表。我们将其命名为tbl_ParentChild。

下面是我的代码:
Declare @Child varchar(10), @RowsEffected int
Set @Child='D'-----It is the member whose family we want to find
 CREATE Table #PrntChld (Parent varchar(10),Child varchar(10))
 Insert Into #PrntChld
 Select Parent,Child  from tbl_ParentChild MF
 Where MF.Child=@Child or MF.Parent=@Child
 Select @RowsEffected=Count(*) from #PrntChld
 While @RowsEffected>0 
 BEGIN
    ;WITH Prnt(Parent,Child)
        AS
        ( Select M.Parent,M.Child  from tbl_ParentChild M
            Inner Join #PrntChld F On F.Child=M.Child
          UNION ALL
          SELECT e.Parent,e.Child       
              FROM tbl_ParentChild AS E
              INNER JOIN  Prnt AS M                 
                  ON E.Child = M.Parent           
        ),
        PrntChld(Parent,Child)
        AS
        ( Select M.Parent,M.Child  from tbl_ParentChild M
            Inner Join (Select * from Prnt union Select * from #PrntChld) F On M.Parent=F.Parent
          UNION ALL
          SELECT e.Parent,e.Child       
              FROM tbl_ParentChild AS E
              INNER JOIN  PrntChld AS M                 
                  ON M.Child = E.Parent           
        )
    Insert Into #PrntChld
    Select distinct MF.* from PrntChld MF 
    Left Join #PrntChld T On T.Child =MF.Child and T.Parent  = MF.Parent    
    where T.Child is null
    Select @RowsEffected=@@ROWCOUNT
END
Select * from #PrntChld
drop table #PrntChld

我将用递归的字符串限制器发布我的解决方案。我想知道对于一个大图表来说,这样做是否对性能有好处,但我认为这并不是那么糟糕。基本上,我正在递归地处理关系,记录每一步的"路径",并检查路径,以便我可以在遇到循环关系时停止。

对于任何想要尝试它的人,我发布了表的创建和填充:

create table nodes
(
    p char, 
    c char
)
insert into nodes (p, c)
values
    ('A', 'B'), ('A', 'C'), ('B', 'D'), ('D', 'F'), ('M', 'F'), ('M', 'N'), ('C', 'E'),
    ('G', 'E'), ('G', 'H'), ('J', 'H'), ('J', 'K'), ('H', 'I'), ('K', 'I'), ('K', 'L'),
    ('R', 'S'), ('S', 'T'), ('U', 'T')

下面是查询:

declare @let char = 'D';
with cte as
(
    --get the node itself if it exists in the table at all
    select 
        top 1 @let as letter, '' as rec_path 
    from 
        nodes 
    where 
        c = @let or p = @let 
    union all
    -- get the direct relations of the node as a starting point
    select 
        case when c = @let then p else c end as letter, 
        cast(@let as varchar(max)) as rec_path
    from 
        nodes 
    where 
        c = @let or p = @let
    union all
    -- get all of the relations recursively until you reach a node you already processed
    select 
        case when c = cte.letter then p else c end as letter, 
        rec_path + cte.letter as rec_path
    from 
        cte
        join nodes on 
            (cte.letter = nodes.c and charindex(cast(nodes.p as varchar(1)), rec_path, 1) = 0 
            or (cte.letter = nodes.p and charindex(cast(nodes.c as varchar(1)), rec_path, 1) = 0))
)
select 
    distinct letter
from 
    cte
我希望它会有用。我意识到您的数据实际上并不由字母组成,但同样可以使用id-s再次使用字符串路径,甚至xml。

最新更新