树结构和递归 [英] Tree Structure and Recursion

查看:25
本文介绍了树结构和递归的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 PostgreSQL 8.4.14 数据库,我有一个表示树结构的表,如下例所示:

Using a PostgreSQL 8.4.14 database, I have a table representing a tree structure like the following example:

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.

I want to create an Access Control List for those units, where each unit may have it's own ACL, or is inheriting it from the nearest ancestor with an own 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:

I'm using a view to determine if a unit is inheriting it's ACL from an ancestor:

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 的最近单元?我的预期结果应该类似于下表/视图:

My question is: how can I get the nearest unit which is NOT inheriting the ACL from an ancestor? My expected result should look similar to the following table/view:

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

推荐答案

使用 递归 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 的行.瞧.

The break condition in the second leg of the UNION is n.acl IS NULL. With that, the query stops traversing the the tree as soon as an acl is found.
In the final SELECT we only return the rows where an acl was found. Voilá.

顺便说一句:使用通用的、非描述性的 id 作为列名是一种反模式.可悲的是,一些 ORM 默认情况下会这样做.将其称为 unit_id,您就不必一直在查询中使用别名.

As an aside: It is an anti-pattern to use the generic, non-descriptive id as column name. Sadly, some ORMs do that by default. Call it unit_id and you don't have to use aliases in queries all the time.

这篇关于树结构和递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆