2014-06-15 68 views
1

遞歸關係,我成立了一個多對多遞歸表是這樣的:一個多對多表

CREATE TABLE PROD 
(
IDPROD INT NOT NULL PRIMARY KEY, 
NAME VARCHAR(3) 
); 

CREATE TABLE COMP 
(
IDPARENT INT REFERENCES PROD(IDPROD), 
IDCHILD INT REFERENCES PROD(IDPROD) 
); 

INSERT INTO PROD (IDPROD, NAME) VALUES (1, 'abc'); 
INSERT INTO PROD (IDPROD, NAME) VALUES (2, 'def'); 
INSERT INTO PROD (IDPROD, NAME) VALUES (3, 'ghi'); 
INSERT INTO PROD (IDPROD, NAME) VALUES (4, 'jkl'); 
INSERT INTO PROD (IDPROD, NAME) VALUES (5, 'mno'); 

INSERT INTO COMP (IDPARENT, IDCHILD) VALUES (1, 2); 
INSERT INTO COMP (IDPARENT, IDCHILD) VALUES (3, 4); 
INSERT INTO COMP (IDPARENT, IDCHILD) VALUES (4, 5); 

隨着recursivs CTE我可以從第二個表中的特定節點的所有孩子。

WITH RECURSIVE TEST (IDPARENT, IDCHILD) AS 
(SELECT P0.IDPARENT, P0.IDCHILD 
FROM COMP AS P0 
WHERE P0.IDPARENT = 3 
UNION ALL 
SELECT P1.IDPARENT, P1.IDCHILD 
FROM COMP AS P1, TEST AS T 
WHERE T.IDCHILD = P1.IDPARENT) 

SELECT * FROM TEST 

但我需要一個查詢,這將使我的整個結構,而不僅僅是一個節點。就像在經典的鄰接列表中,您可以獲得IDPARENT爲NULL的所有根節點以及下面列出的子節點。我使用火鳥。

+0

如果你想表示一棵樹,你不需要一個:M的關係,1:N會做因爲所有節點都只有一個(!)父節點(根節點有一個空父節點) – alzaimar

+0

在我的情況下,它必須是多對多的,因爲許多PROD(產品)可以具有相同的COMP(組件)。 – JohnAPaul

+1

ok,從技術上講,它不是一棵樹,而是一個有向圖 – alzaimar

回答

3

我不熟悉的火鳥,但這部作品在SQL Server中,所以是希望類似/足以讓您的軌道上:

WITH TEST (IDRoot, IDPARENT, IDCHILD) AS 
(

    SELECT P0.IDPROD, C0.IDParent, C0.IDCHILD 
    FROM PROD AS P0 
    left outer join COMP C0 on C0.IDParent = P0.IDPROD 
    WHERE P0.IDProd not in (select IDChild from COMP) 

    UNION ALL 

    SELECT T.IDRoot, C1.IDPARENT, C1.IDCHILD 
    FROM COMP AS C1 
    inner join TEST AS T on T.IDCHILD = C1.IDPARENT 

) 
SELECT * FROM TEST 

希望有所幫助。

SQL小提琴版本:http://sqlfiddle.com/#!6/22f84/7

注意

Includ列來表示樹的根,以及父/子 - 因爲可能有多個樹,如果我們不指定特定的根:

WITH TEST (IDRoot, IDPARENT, IDCHILD) AS 

治療任何產品,其是不是小孩作爲根(即,第一項在樹)。

WHERE P0.IDProd not in (select IDChild from COMP) 

編輯:回答任何節點以查看其所有的親戚在評論

查詢:

簡單的方法來過濾任何節點上會修改上面的語句的WHERE P0.IDProd not in (select IDChild from COMP)WHERE P0.IDProd = IdImInterestedIn。但是,如果您想使用CTE查看視圖,則可以通過此靜態查詢運行查詢,您可以使用下面的代碼 - 然後,您可以在IDProdselect * from test where IDProd = IdImInterestedIn)上篩選以查看該項目的祖先和後代。

WITH TEST (IDProd, IDRelation, Generation) AS 
(

    SELECT IDPROD 
    , IDPROD 
    , 0 
    FROM PROD 

    UNION ALL 

    SELECT T.IDPROD 
    , C.IdParent 
    , T.Generation - 1 
    FROM TEST AS T 
    inner join Comp as C 
    on C.IdChild = T.IDRelation 
    where t.Generation <= 0 

    UNION ALL 

    SELECT T.IDPROD 
    , C.IdChild 
    , T.Generation + 1 
    FROM TEST AS T 
    inner join Comp as C 
    on C.IdParent = T.IDRelation 
    where t.Generation >= 0 

) 
SELECT * 
FROM TEST 
order by IDProd, Generation 

SQL小提琴:http://sqlfiddle.com/#!6/22f84/15

見根節點的全樹一列

WITH TEST (IDRoot, IDPARENT, IDCHILD, TREE) AS 
(

    SELECT P0.IDPROD, C0.IDParent, C0.IDCHILD, cast(P0.IDPROD as nvarchar(max)) + coalesce(', ' + cast(C0.IDCHILD as nvarchar(max)),'') 
    FROM PROD AS P0 
    left outer join COMP C0 on C0.IDParent = P0.IDPROD 
    WHERE P0.IDProd not in (select IDChild from COMP) 

    UNION ALL 

    SELECT T.IDRoot, C1.IDPARENT, C1.IDCHILD, TREE + coalesce(', ' + cast(C1.IDCHILD as nvarchar(max)),'') 
    FROM COMP AS C1 
    inner join TEST AS T on T.IDCHILD = C1.IDPARENT 

) 
SELECT * 
FROM TEST 
order by IDRoot 

SQL小提琴:http://sqlfiddle.com/#!6/22f84/19

編輯:回答到其他意見

with cte (tree_root_no, tree_row_no, relation_sort, relation_chart, Name, id, avoid_circular_ref) as 
(
     select row_number() over (order by p.idprod) 
     , 1 
     , cast(row_number() over (order by p.idprod) as nvarchar(max)) 
     , cast('-' as nvarchar(max)) 
     , p.NAME 
     , p.IDPROD 
     , ',' + cast(p.IDPROD as nvarchar(max)) + ',' 
     from PROD p 
     where p.IDPROD not in (select IDCHILD from COMP) --if it's nothing's child, it's a tree's root 

     union all 

     select cte.tree_root_no 
     , cte.tree_row_no + 1 
     , cte.relation_sort + cast(row_number() over (order by p.idprod) as nvarchar(max)) 
     , replace(relation_chart,'-','|') + ' -' 
     , p.NAME 
     , p.IDPROD 
     , cte.avoid_circular_ref + cast(p.IDPROD as nvarchar(max)) + ',' 
     from cte 
     inner join COMP c on c.IDPARENT = cte.id 
     inner join PROD p on p.IDPROD = c.IDCHILD 
     where charindex(',' + cast(p.IDPROD as nvarchar(max)) + ',', cte.avoid_circular_ref) = 0 
) 
select tree_root_no, tree_row_no, relation_sort, relation_chart, id, name 
from cte 
order by tree_root_no, relation_sort 

SQL小提琴:http://sqlfiddle.com/#!6/4397f/9

更新,以顯示每路

這一個是一個討厭的黑客,但我能想到的唯一的辦法來解決你的難題;這給每個路徑穿過樹林自己的號碼:

;with inner_cte (parent, child, sequence, treePath) as (

    select null 
    , p.IDPROD 
    , 1 
    , ',' + CAST(p.idprod as nvarchar(max)) + ',' 
    from @prod p 
    where IDPROD not in 
    (
     select IDCHILD from @comp 
    ) 

    union all 

    select cte.child 
    , c.IDCHILD 
    , cte.sequence + 1 
    , cte.treePath + CAST(c.IDCHILD as nvarchar(max)) + ',' 
    from inner_cte cte 
    inner join @comp c on c.IDPARENT = cte.child 

) 
, outer_cte (id, value, pathNo, sequence, parent, treePath) as 
(
    select icte.child, p.NAME, ROW_NUMBER() over (order by icte.child), icte.sequence, icte.parent, icte.treePath 
    from inner_cte icte 
    inner join @prod p on p.IDPROD = icte.child 
    where icte.child not in (select coalesce(icte2.parent,-1) from inner_cte icte2) 

    union all 

    select icte.child, p.NAME, octe.pathNo,icte.sequence, icte.parent, icte.treePath 
    from outer_cte octe 
    inner join inner_cte icte on icte.child = octe.parent and CHARINDEX(icte.treePath, octe.treePath) > 0 
    inner join @prod p on p.IDPROD = icte.child 

) 
select id, value, pathNo 
from outer_cte 
order by pathNo, sequence 

SQL小提琴這裏:http://sqlfiddle.com/#!6/5a16e/1

+0

它可以工作,但是在Firebird中你必須添加RE CURSIVE WITH後,否則你會得到一個「CTE'TEST'有循環依賴關係」的錯誤。 我有兩個問題:給定一個節點,我如何查詢所有的後代和祖先?是否可以列出樹木的結構?例如,如果我添加 INSERT INTO COMP(IDPARENT,IDCHILD)VALUES(1,4); 我可以有這樣的專欄嗎? '1 - 根 2 - 葉 1 - 根 5 - 葉 3 - 根 5 - 葉' 視覺 http://postimg.org/ image/j6z5s86v3/ – JohnAPaul

+0

非常感謝你的答案。我只希望有一天我會擁有這種SQL技能。關於我的第二個問題,我正在考慮其他問題。每個數字都是一列中的一行,但是stackoverflow格式化了輸出。這就是我發現你在評論中不能有換行符的原因。只是一個快樂的事故,我猜想:)我想要的是把樹放在同一列,每個節點放在一個單獨的行上。我更新了圖像。 http://postimg.org/image/57fwkuf5r/ – JohnAPaul

+0

認爲我有一個解決方案 - 但這是一個非常棘手的,所以代碼有點哈克:http://sqlfiddle.com/#!6/5a16e/1 – JohnLBevan