【问题标题】:Retrieve hierarchical groups ... with infinite recursion检索分层组...无限递归
【发布时间】:2013-07-30 16:25:39
【问题描述】:

我有一个这样的表格,其中包含链接:

key_a    key_b
--------------
a        b        
b        c
g        h     
a        g       
c        a
f        g

不是很整洁和无限递归...

key_a = 父级 key_b = 孩子

需要一个查询,该查询将为每个层次组(父级 + 直接子级 + 间接子级)重新组合和分配一个数字:

key_a    key_b    nb_group
--------------------------
a        b        1
a        g        1
b        c        1
**c        a**        1
f        g        2
g        h        2

**link responsible of infinite loop**

因为我们有

A-B-C-A

-> 只想简单地显示如图所示的链接。

有什么想法吗?

提前致谢

【问题讨论】:

  • 更新:无限递归问题
  • “无限递归”是什么意思?层次结构没有理论上的限制?或者层次结构在某个点循环,使得某些分支上实际上没有无子节点? --Edit:看起来像后者,那么当你到达一个循环时你想发生什么?
  • 因为在某些情况下父母可能是孩子
  • 我不明白nb_group 是什么。是父母的水平吗?如果是这样,在第 3 行不应该是 2 吗?
  • 如何知道链是否以A 开头而不是A-B-C-A-B-... 中的BC

标签: sql sql-server-2008


【解决方案1】:

问题在于您并没有真正处理严格的层次结构;你正在处理有向图,其中一些图有循环。请注意,您的 nbgroup #1 没有任何规范根 - 由于来自 c-a 的循环引用,它可能是 a、b 或 c。

处理这个问题的基本方法是从图形技术的角度来思考,而不是递归。事实上,迭代方法(不使用 CTE)是我在 SQL 中能想到的唯一解决方案。基本方法是explained here

Here is a SQL Fiddle 具有解决循环和共享叶情况的解决方案。请注意,它使用迭代(使用故障保护来防止进程失控)和表变量来操作;我不认为有任何解决这个问题。另请注意更改后的样本数据(a-g 更改为 a-h;如下所述)。

如果您深入研究 SQL,您会注意到我从链接中给出的解决方案中更改了一些关键内容。该解决方案是处理无向边,而您的边是有向的(如果您使用无向边,则由于 a-g 连接,整个样本集是单个组件)。

这正是我在示例数据中将 a-g 更改为 a-h 的核心。如果只共享叶节点,您对问题的说明很简单;这就是我编码的规范。在这种情况下, a-h 和 g-h 都可以毫无问题地捆绑到它们适当的组件上,因为我们担心父级的可达性(即使是给定的周期)。

但是,当您有共享分支时,不清楚您要显示什么。考虑 a-g 链接:鉴于此,g-h 可以存在于任一组件中(a-g-h 或 f-g-h)。你把它放在第二个,但它本来可以放在第一个,对吧?这种模棱两可是我没有尝试在这个解决方案中解决它的原因。

编辑:要清楚,在我上面的解决方案中,如果遇到共享分支,它将整个集合视为单个组件。不是你上面描述的,但问题解决后必须改变它。希望这能让你接近。

【讨论】:

    【解决方案2】:

    您应该使用递归查询。在第一部分中,我们选择所有顶级节点(没有父节点)的记录,并使用ROW_NUMBER() 为它们分配组 ID 号。然后在递归部分,我们一个一个地添加孩子,并使用父母的组 ID 号。

    with CTE as 
    (
    
    select t1.parent,t1.child,
           ROW_NUMBER() over (order by t1.parent) rn
    
    from t t1 where 
    not exists (select 1 from t where child=t1.parent)
    union all
    select t.parent,t.child, CTE.rn
    from t  
    join CTE on t.parent=CTE.Child  
    )
    select * from CTE
    order by RN,parent
    

    SQLFiddle demo

    【讨论】:

    • 看起来很完美,但我想我还有另一个问题......有些团体可以无限回避
    • @ViséeMaxence:您需要检查与孩子的联合以限制您的递归。 stackoverflow.com/a/660145/128217
    【解决方案3】:

    使用递归 CTE 进行图行走的痛苦问题。这是在图中找到连通子图的问题。使用递归 CTE 的挑战是防止不必要的递归 - 即 SQL Server 中的无限循环,这通常意味着将它们存储在字符串中。

    这个想法是获取所有连接的节点对的列表(并且一个节点与其自身连接)。然后,从连接节点列表中取最小值,并将其用作连接子图的 id。

    另一个想法是从一个节点在两个方向上遍历图形。这确保访问所有可能的节点。以下是完成此操作的查询:

    with fullt as (
          select keyA, keyB
          from t
          union
          select keyB, keyA
          from t
         ),
         CTE as (
          select t.keyA, t.keyB, t.keyB as last, 1 as level,
                 ','+cast(keyA as varchar(max))+','+cast(keyB as varchar(max))+',' as path
          from fullt t
          union all
          select cte.keyA, cte.keyB,
                 (case when t.keyA = cte.last then t.keyB else t.keyA
                  end) as last,
                 1 + level,
                 cte.path+t.keyB+','
          from fullt t join
               CTE
               on t.keyA = CTE.last or
                  t.keyB = cte.keyA 
          where cte.path not like '%,'+t.keyB+',%'
         ) -- select * from cte where 'g' in (keyA, keyB)
    select t.keyA, t.keyB,
           dense_rank() over (order by min(cte.Last)) as grp,
           min(cte.Last)
    from t join
         CTE
         on (t.keyA = CTE.keyA and t.keyB = cte.keyB) or
            (t.keyA = CTE.keyB and t.keyB = cte.keyA)
    where cte.path like '%,'+t.keyA+',%' or
          cte.path like '%,'+t.keyB+',%'
    group by t.id, t.keyA, t.keyB
    order by t.id;
    

    SQLFiddle 是 here

    【讨论】:

    • 我注意到您的解决方案在共享分支方面与我的问题相同。我的尊重共享叶节点(作为两个独立组件的一部分),但是像您的一样,当共享更多组件时,将合并两个组件。尽管如此,我还是有兴趣看到 CTE 的实施——干得好!
    【解决方案4】:

    你可能想用 COMMON TABLE EXPRESSIONS 检查一下

    here's the link

    【讨论】:

      猜你喜欢
      • 2012-03-26
      • 1970-01-01
      • 2011-05-10
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-17
      • 2015-01-09
      相关资源
      最近更新 更多