树形结构和递归

21

使用 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 非常好的问题。像总是一样,您的PostgreSQL版本应该包括在内。 - Erwin Brandstetter
1个回答

19

使用递归公共表达式(CTE)的查询可以完成此工作。需要 PostgreSQL8.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。由此,查询在找到后立即停止遍历树。
在最终的SELECT中,我们只返回找到的行。就这样。
顺便说一下:使用通用的、不具描述性的id作为列名是一种反模式。不幸的是,一些ORM默认使用它。将其命名为unit_id,您就不必一直在查询中使用别名了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接