我有一个这样的表,其中包含链接:
key_a key_b
--------------
a b
b c
g h
a g
c a
f g
不是很整洁&无限递归...
key_a = parent key_b = child
需要一个查询,它将为每个层次组(父级+直接子级+间接子级)重新组合和归属一个数字:
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
- >只想显示如图所示的链接。
有什么想法吗?
提前致谢
答案 0 :(得分:5)
问题在于你并没有真正处理严格的层次结构;你正在处理有向图,其中一些图有循环。请注意,您的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)中。你把它放在第二个,但它可能在第一个,而不是?这种含糊不清是为什么我没有尝试在这个解决方案中解决它。
编辑:要清楚,在上面的解决方案中,如果遇到共享分支,它会将整个集合视为单个组件。不是你上面描述的,但在问题澄清后必须改变。希望这能让你接近。
答案 1 :(得分: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
答案 2 :(得分: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是here。
答案 3 :(得分:-1)
您可能想要查看COMMON TABLE EXPRESSIONS