2012-11-01 195 views
12

使用的PostgreSQL數據庫8.4.14,我有一個表代表一個樹狀結構類似於下面的示例:樹結構和遞歸

CREATE TABLE unit (
    id bigint NOT NULL PRIMARY KEY, 
    name varchar(64) NOT NULL, 
    parent_id bigint, 
    FOREIGN KEY (parent_id) REFERENCES unit (id) 
); 
INSERT INTO unit VALUES (1, 'parent', NULL), (2, 'child', 1) 
         , (3, 'grandchild A', 2), (4, 'grandchild B', 2); 
id | name  | parent_id 
----+--------------+----------- 
    1 | parent  |   
    2 | child  |   1 
    3 | grandchild A |   2 
    4 | grandchild B |   2 

我要爲這些單位創建一個訪問控制列表,每個單元可能擁有自己的ACL,或者使用自己的ACL從最近的祖先繼承它。

CREATE TABLE acl (
    unit_id bigint NOT NULL PRIMARY KEY, 
    FOREIGN KEY (unit_id) REFERENCES unit (id) 
); 
INSERT INTO acl VALUES (1), (4); 
unit_id 
--------- 
     1 
     4 

我使用視圖,以確定是否一個單元繼承它的ACL從一個祖先:

CREATE VIEW inheriting_acl AS 
    SELECT u.id AS unit_id, COUNT(a.*) = 0 AS inheriting 
    FROM unit AS u 
    LEFT JOIN acl AS a ON a.unit_id = u.id 
    GROUP BY u.id; 
unit_id | inheriting 
---------+------------ 
     1 | f 
     2 | t 
     3 | t 
     4 | f 

我的問題是:我怎麼能得到最近的單元是不是從祖先繼承ACL?我預期的結果應類似於如下表/視圖:

unit_id | acl 
---------+------------ 
     1 | 1 
     2 | 1 
     3 | 1 
     4 | 4 
+2

+1非常好的問題。 As * always *,您的PostgreSQL版本應該包含在內。 –

回答

12

的查詢與recursive CTE可以做的工作。需要的PostgreSQL 8.4或更新:

WITH RECURSIVE next_in_line AS (
    SELECT u.id AS unit_id, u.parent_id, a.unit_id AS acl 
    FROM unit u 
    LEFT JOIN acl a ON a.unit_id = u.id 

    UNION ALL 
    SELECT n.unit_id, u.parent_id, a.unit_id 
    FROM next_in_line n 
    JOIN unit u ON u.id = n.parent_id AND n.acl IS NULL 
    LEFT JOIN acl a ON a.unit_id = u.id 
    ) 
SELECT unit_id, acl 
FROM next_in_line 
WHERE acl IS NOT NULL 
ORDER BY unit_id 

UNION的第二腿斷點條件爲n.acl IS NULL。這樣,只要找到acl,查詢就會停止遍歷樹。
在最後的SELECT中,我們只返回發現了acl的行。瞧。

另一方面:使用通用的非描述性id作爲列名是反模式。可悲的是,一些ORM默認這樣做。將其稱爲unit_id,您不必在查詢中始終使用別名。

+0

完美,謝謝! –