8

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

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**

因为我们有

ABCA

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

任何想法 ?

提前致谢

4

4 回答 4

5

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

处理这个问题的基本方法是考虑图技术,而不是递归。事实上,迭代方法(不使用 CTE)是我在 SQL 中能想到的唯一解决方案。这里解释了基本方法。

这是一个 SQL Fiddle,它提供了一个解决循环和共享叶案例的解决方案。请注意,它使用迭代(带有故障保护以防止进程失控)和表变量来操作;我不认为有任何解决这个问题。还要注意更改的样本数据(ag 更改为 ah;解释如下)。

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

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

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

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

于 2013-08-01T15:34:30.453 回答
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 演示

于 2013-07-30T13:40:58.260 回答
0

使用递归 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 在这里

于 2013-08-03T19:23:11.110 回答
-1

你可能想检查一下常用表表达式

这是链接

于 2013-08-05T01:31:37.663 回答