【问题标题】:CTE for parent child relation with multiple parent与多个父母的父子关系的 CTE
【发布时间】:2014-07-01 05:43:32
【问题描述】:

我有一个父子关系表,如下所示。我想像所有祖先和父母一样检索父母或孩子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

谢谢,

喜马拉雅

【问题讨论】:

  • 我知道可以使用递归查询检索所有直系后代。我很想知道是否有人对这样的任意父/子关系的完整图表有解决方案。
  • 循环图 :) 我最近遇到了这种性质的问题。随着rCTE 的进展,我被困在有效地记录游览\遍历历史(并查询它)上。按照此处的建议使用WHILE 循环登陆:hansolav.net/sql/graphs.html - 将密切关注这篇文章,看看它会发生什么。

标签: sql tsql sql-server-2005 graph common-table-expression


【解决方案1】:

此解决方案仅适用于非循环图,因为递归将在循环时中断,采用提供的数据集...

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

这对于面包屑路径之类的东西很方便

【讨论】:

    【解决方案2】:

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

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

    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。

    【讨论】:

      【解决方案3】:

      我找到了解决方案。但我在 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-04
        相关资源
        最近更新 更多