PostgreSQL:查找元素的权限,遍历到root

时间:2016-06-30 10:53:21

标签: postgresql recursive-query

以下是使用

的数据库结构
Item
----
ID [PK]
Name
Desc

Links
-----
ID [FK]
LID [FK] -- Link ID
LType    -- Link Type (Parent, Alias)

Permission 
----------
ID [FK]
CanRead
CanWrite
CanDelete


Let's assume, we have the below data in the table
Item Table
-----------
ID  Name    Desc
=================
0   Root    Base Item
1   One     First
2   Two     Second
3   Three   Third
4   Four    Forth
5   Five    Fifth
6   Six     Sixth

Links Table
-----------
ID  LID     LType   
==================
1   0       Parent
2   0       Parent
3   1       Parent
4   2       Parent
5   4       Parent
6   5       Parent

0 
|- 1 
|   |- 3
|- 2
    |- 4
        |- 5
            |- 6

Permission Table
-----------------
ID  CanRead     CanWrite    CanDelete
=====================================
0   T           T           T
2   T           F           F
5   T           T           F
6   F           F           F

问题是,如果我想要6的权限,我可以直接查询Permission表并获取Read / Write / Delete值。 但是,如果我想要4的权限,它在权限表中不存在,所以我需要找到父,即2, 因为我有2个人的许可,所以我可以退货。

更棘手, 如果我想要3的权限,我签入权限表,它不存在,请参阅不存在的父(1), 去它的父母是(0-Root),然后返回值。

这可以适用于任何级别,假设我们在权限表中没有记录2,5,6, 所以当我查找6时,我需要一直遍历到root以获得权限。

注意:我们始终拥有Root的权限。

我希望在数据库层而不是应用程序层完成此任务,因此编写SQL查询(递归)或存储过程的任何帮助都会很棒。

谢谢!

2 个答案:

答案 0 :(得分:2)

您可以使用RECURSIVE CTE

WITH RECURSIVE Perms(ID, Name, ParentID, CanRead, CanWrite, CanDelete) AS (
   SELECT i.ID, i.Name, l.LID AS ParentID, p.CanRead, p.CanWrite, p.CanDelete
   FROM Item AS i
   LEFT JOIN Permission AS p ON i.ID = p.ID
   LEFT JOIN Links AS l ON i.ID = l.ID
),  GET_PERMS(ID, ParentID, CanRead, CanWrite, CanDelete) AS (
    -- Anchor member: Try to get Read/Write/Delete values from Permission table
    SELECT ID, ParentID, CanRead, CanWrite, CanDelete
    FROM Perms
    WHERE ID = 3

  UNION ALL

    -- Recursive member: terminate if the previous level yielded a `NOT NULL` result
    SELECT p.ID, p.ParentID, p.CanRead, p.CanWrite, p.CanDelete
    FROM GET_PERMS AS gp 
    INNER JOIN Perms AS p ON gp.ParentID = p.ID    
    WHERE gp.CanRead IS NULL
)
SELECT CanRead, CanWrite, CanDelete 
FROM GET_PERMS
WHERE CanRead IS NOT NULL

从数据库中检索到RECURSIVE CTE记录时Permission终止。

Demo here

答案 1 :(得分:1)

WITH RECURSIVE tree AS (
        SELECT i.id AS my_id
                , p.id AS perm_id
        FROM items i
        JOIN permission p ON p.id = i.id
        WHERE i.id = 0 -- root
        UNION ALL
        SELECT l.id AS my_id
                , COALESCE (p.id,t.perm_id) AS perm_id
        FROM links l
        JOIN tree t ON l.lid = t.my_id
        LEFT JOIN permission p ON p.id = l.id
        )
SELECT i.*, t.perm_id
        , p.canread, p.canwrite, p.candelete
FROM items i
JOIN tree t ON t.my_id = i.id
JOIN permission p ON t.perm_id = p.id
        ;

结果:

CREATE TABLE
CREATE TABLE
CREATE TABLE
INSERT 0 7
INSERT 0 6
INSERT 0 4
 id | name  |   descr   | perm_id | canread | canwrite | candelete 
----+-------+-----------+---------+---------+----------+-----------
  0 | Root  | Base Item |       0 | t       | t        | t
  1 | One   | First     |       0 | t       | t        | t
  2 | Two   | Second    |       2 | t       | f        | f
  3 | Three | Third     |       0 | t       | t        | t
  4 | Four  | Forth     |       2 | t       | f        | f
  5 | Five  | Fifth     |       5 | t       | t        | f
  6 | Six   | Sixth     |       6 | f       | f        | f
(7 rows)

仅供参考:这是树木行走,不太优雅,IMO

WITH RECURSIVE tree_up AS (
        SELECT i.id AS my_id
                , p.id AS perm_id
        FROM items i
        LEFT JOIN permission p ON p.id = i.id
        WHERE i.id = 3 -- << PARAMETER: starting point
        UNION ALL
        SELECT l.lid AS my_id
                , COALESCE (t.perm_id,p.id) AS perm_id
        FROM tree_up t
        JOIN links l ON l.id = t.my_id
        LEFT JOIN permission p ON p.id = l.lid
        WHERE t.perm_id IS NULL
        )
SELECT i.*, t.perm_id
        , p.canread, p.canwrite, p.candelete
FROM items i
JOIN tree_up t ON t.my_id = i.id
JOIN permission p ON t.perm_id = p.id
        ;