这是一个适用于任意深度的解决方案。
将所有关系存储在一张表中:
Table t
Parent | Child
------ | -----
B | A
C | B
A | C
E | D
F | E
然后您可以使用此WITH RECURSIVE 查询来查找循环:
WITH RECURSIVE working(parent, last_visited, already_visited, cycle_detected) AS (
SELECT parent, child, ARRAY[parent], false FROM t
UNION ALL
SELECT t.parent, t.child, already_visited || t.parent, t.parent = ANY(already_visited)
FROM t
JOIN working ON working.last_visited = t.parent
WHERE NOT cycle_detected
)
SELECT parent, already_visited FROM working WHERE cycle_detected
Fiddle
它将为您提供属于循环一部分的parents,以及它们所在的循环:
A | A,C,B,A
B | B,A,C,B
C | C,B,A,C
它是这样工作的(因为这是关键字 RECURSIVE 指示 Postgres 做的事情):
- 运行第一个
SELECT,从表t 中选择所有条目并将它们放在一个名为working 的临时表中。
- 然后运行第二个
SELECT,将working 表与t 表连接起来以查找每个条目的子项。这些孩子被添加到已经见过的孩子数组中。
- 现在,只要将条目添加到
working 表中,就一次又一次地运行第二个 SELECT。
- 当其中一个条目访问它之前访问过的子项 (
t.parent = ANY(already_visited)) 时检测到一个循环,在这种情况下,cycle_detected 设置为 true,并且不再向条目添加子项。