2016-09-18 81 views
1

我想檢測層次結構中的潛在週期。我有三個表,每個表有一個父,和一個孩子柱:使用sql檢測週期

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

表1中包含一些節點(列名兒童)和他們的父母(在列父); Table2包含Table1的所有父項(在子列中)及其父項(在父列中)等等。

例如,如果A是B的子項,以及B是C的子和C是A的一個子,然後我有一個週期。

是否有可能檢測到使用SQL命令的週期?

+0

您正在使用哪個數據庫管理系統? –

+0

我正在使用Postgres。你是這個意思嗎? – geek2000

+0

你是什麼意思的週期?你能否提高效率 –

回答

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 s表示是一個循環的一部分,也是他們所處的循環:

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

它的工作原理是這樣的(因爲這是關鍵字RECURSIVE指示Postgres的做):

  1. 運行第一SELECT,從表t選擇所有條目,將它們放置在一個名爲working臨時表。
  2. 然後運行第二個SELECT,與表t加入working表中查找每個條目的孩子。這些孩子被添加到已經看過的孩子們的陣列中。
  3. 現在連連運行第二個SELECT,只要輸入被添加到working表。當其中一個條目在這種情況下cycle_detected設置爲true訪問它以前(t.parent = ANY(already_visited))參觀了一個孩子,沒有更多的孩子加入到項檢測
  4. 一個週期。
+0

謝謝你的偉大的東西,併爲您的全面解釋。很有用。 – geek2000

0

您的任務中的表格之間有非常奇怪的引用。然而,這是我的方法來檢查現有的循環。

示例表1:

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(); 
+0

非常感謝!我不熟悉plpgsql。我會研究你的代碼。 – geek2000

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; 
+0

如果您的查詢沒有返回任何內容,則表示沒有循環,對不對? – geek2000

+0

是的。它應該返回所有現有的週期,所以如果它什麼都不返回,那麼就沒有周期。 – mateuszlo

+0

非常感謝! – geek2000