使用 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
具有递归 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找到an 的行。瞧。
UNION
n.acl IS NULL
acl
SELECT
顺便说一句:使用通用的、非描述性的id作为列名是一种反模式。可悲的是,一些 ORM 默认情况下会这样做。调用它unit_id,您不必一直在查询中使用别名。
id
unit_id