3

我需要使用遞歸CTE迭代帶有循環的圖。循環的遞歸CTE停止條件

問題是迴路部分。

我想要如果有循環,然後選擇最短的路徑。 這基本上意味着忽略循環,因爲遞歸是「寬度優先」。

下面的示例顯示了返回的數據:

的問題是註釋掉INSERT語句創建了一個循環。 顯然,它沒有註釋,查詢將永遠不會結束。

我需要的是返回與沒有循環相同的數據。

DROP TABLE IF EXISTS edges; 

CREATE TABLE edges(
    src integer, 
    dst integer, 
    data integer 
); 

INSERT INTO edges VALUES (1, 2, 1); 
INSERT INTO edges VALUES (2, 3, 1); 
--INSERT INTO edges VALUES (3, 2, 1); -- This entry creates a loop 
INSERT INTO edges VALUES (1, 4, 1); 
INSERT INTO edges VALUES (4, 5, 1); 
INSERT INTO edges VALUES (5, 2, 1); 

INSERT INTO edges VALUES (1, 4, 2); 
INSERT INTO edges VALUES (4, 5, 2); 
INSERT INTO edges VALUES (4, 6, 2); 


WITH RECURSIVE paths AS (
    -- For simplicity assume node 1 is the start 
    -- we'll have two starting nodes for data = 1 and 2 
    SELECT DISTINCT 
    src   as node 
    , data  as data 
    , 0   as depth 
    , src::text as path 
    FROM edges 
    WHERE 
    src = 1 

    UNION ALL 

    SELECT DISTINCT 
    edges.dst 
    , edges.data 
    , depth + 1 
    , paths.path || '->' || edges.dst::text 
    FROM paths 
    JOIN edges ON edges.src = paths.node AND edges.data = paths.data 
    -- AND eliminate loops? 
) 

SELECT * FROM paths; 

返回:

node | data | depth |  path  
------+------+-------+--------------- 
    1 | 1 |  0 | 1 
    1 | 2 |  0 | 1 
    2 | 1 |  1 | 1->2 
    4 | 1 |  1 | 1->4 
    4 | 2 |  1 | 1->4 
    3 | 1 |  2 | 1->2->3 
    5 | 2 |  2 | 1->4->5 
    6 | 2 |  2 | 1->4->6 
    5 | 1 |  2 | 1->4->5 
    2 | 1 |  3 | 1->4->5->2 
    3 | 1 |  4 | 1->4->5->2->3 
(11 rows) 
+0

帶有遞歸CTE的有向循環圖的最短路徑遍歷。耐人尋味。快速搜索找到很多關於這個話題寫的... –

+0

感謝您刪除重複的帖子和一個有趣的一個在這裏; +1,如果我得到時間,將與此玩。 –

+0

我已經有了答案(這是非常明顯和簡單的),但不會在這裏提供:)想看其他選項。 –

回答

1
WITH RECURSIVE paths AS (
    -- For simplicity assume node 1 is the start 
    -- we'll have two starting nodes for data = 1 and 2 
    SELECT DISTINCT 
     src   as node 
     , data  as data 
     , 0   as depth 
     , src::text as path 
     , ''   as edgeAdded 
    FROM edges 
    WHERE 
     src = 1 

    UNION ALL 

    SELECT DISTINCT 
     edges.dst 
     , edges.data 
     , depth + 1 
     , paths.path || '->' || edges.dst::text 
     , edges.src::text || '->' || edges.dst::text 
    FROM paths 
    JOIN edges ON edges.src = paths.node AND edges.data = paths.data 
    AND NOT paths.path LIKE '%' || edges.dst::text || '%' 
     -- AND eliminate loops? 
) 
SELECT * FROM paths; 

這裏與條件AND NOT paths.path LIKE '%' || edges.dst::text || '%'我們避免背部邊緣,這將導致一個循環。
http://www.sqlfiddle.com/#!12/086ee/1

+0

這就是我的解決方案。除了你的將永遠不會完成循環到起始節點。但這很容易解決。儘管如此,我還是會等待解決方案的其他選項。 –