使用sql检测周期

时间:2016-09-18 08:28:56

标签: sql postgresql hierarchical-data

我想检测层次结构中的潜在周期。我有三个表,每个表有一个父表和一个子列:

Table1's parents are Table2's children and Table2's parents are Table3's children

Table1包含一些节点(在列子列中)及其父节点(在父列中); Table2包含Table1的所有父项(在列子项中)及其父项(在列父项中),依此类推。

例如,如果A是B的孩子,而B是C的孩子而C是A的孩子,那么我就有一个周期。

是否可以使用sql命令检测周期?

3 个答案:

答案 0 :(得分:2)

您现在构建表的方式,以下SQL应该可以工作:

SELECT * FROM Table1
INNER JOIN Table2 on Table1.child = Table2.parent
INNER JOIN Table3 on Table2.child = Table3.parent
WHERE Table1.parent = Table3.child;

答案 1 :(得分:1)

这是一个适用于任意深度的解决方案。

将您的所有关系存储在一个表中:

   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

它将为您提供属于周期的parent以及它们所处的周期:

A | A,C,B,A
B | B,A,C,B
C | C,B,A,C

它的工作原理如下(因为这是关键字RECURSIVE指示Postgres做的事情):

  1. 运行第一个SELECT,选择表t中的所有条目,并将它们放在名为working的临时表中。
  2. 然后运行第二个SELECT,将working表加入表t以查找每个条目的子项。这些孩子被添加到已见过的孩子们中。
  3. 现在一次又一次地运行第二个SELECT,只要将条目添加到working表中。
  4. 当其中一个条目访问之前访问过的孩子(t.parent = ANY(already_visited))时,会检测到一个周期,在这种情况下cycle_detected设置为true,并且不再向该条目添加子项。< / LI>

答案 2 :(得分:0)

您的任务中的表之间存在非常奇怪的引用。然而,这是我检查现有循环的方法。

table1的示例:

CREATE OR REPLACE FUNCTION fn_table1_check() RETURNS trigger
LANGUAGE plpgsql
AS $$
DECLARE
BEGIN
  PERFORM 1 FROM table2
    JOIN table3 ON table3.parent=table2.child
  WHERE table2.parent=NEW.child AND table3.child=NEW.parent
  LIMIT 1;
  IF FOUND THEN
    RAISE EXCEPTION 'Found recursive loop!';
  END IF;
  RETURN NEW;
END;
$$;
CREATE TRIGGER tg_table1_check BEFORE INSERT OR UPDATE ON table1 FOR EACH ROW EXECUTE PROCEDURE fn_table1_check();