具有分层 parents 最小值的 Postgresql select 列

Postgresql select column with minimum value of hierarchical parents

有这样的table

                       Table "public.access_level"
  Column   |  Type   | Modifiers | Storage  | Stats target | Description
-----------+---------+-----------+----------+--------------+-------------
 uid       | uuid    | not null  | plain    |              |
 parent_id | integer | not null  | plain    |              |
 child_id  | integer | not null  | plain    |              |
 level     | integer | not null  | plain    |              |
 entity    | text    |           | extended |              |

和这样的行(删除了 uid 列)

 parent_id | child_id | level | entity
-----------+----------+-------+--------
        11 |       22 |     4 | a
        22 |       33 |     5 | a
        33 |       44 |     6 | a
        11 |       22 |     7 | b
        22 |       33 |     4 | b
        33 |       44 |     5 | b

我想要一个输出,其中每行的 returns level 值基于每个 [=17= 独特的 parents 的最小值 level ]. 这是我想要的输出:

 parent_id | child_id | level | entity
-----------+----------+-------+--------
        11 |       22 |     4 | a
        22 |       33 |     4 | a
        33 |       44 |     4 | a
        11 |       22 |     7 | b
        22 |       33 |     4 | b
        33 |       44 |     4 | b

递归方法是可取的,因为层次结构深度不固定。

注:(parent_id,child_id,entity)在table

实际上parent_id和child_id是用户。 a parent 为 a child 提供实体的访问级别。然后 child 用户可以授予另一个 child 用户一定级别的访问权限。在某些时候,parent 的 parent 可能会更改访问级别,如果它是 child。现在所有更深的 children 都必须具有不超过该级别的访问权限。因为parent的parent可能会回滚更改,所以无法使用触发器来更新相应的行。

一个场景:

  • 11,22,7,b 表示 user-11 将 7 的 level 给 user-22 给 b 实体。
  • 现在 user-22 在某个时候将 5 的 user-33 级别提供给 b 实体
  • 然后 user-33 将 5 的等级赋予 user-44 b 实体。
  • 重要提示: 用户 22 将 b 的访问级别更改为 4 对于 user-33,这就是您在示例 table
  • 中看到的
  • b 实体 的 user-33 到 user-44 的访问级别 应该 在 table
  • 中保持 5

但我想要一个查询,该查询将为该列 return 4,就好像我递归地对级别超过 4 的 user-22 的所有 children 执行此操作一样。

谢谢

递归查询的初始部分查找根(没有父节点的节点),在递归部分我们简单地为实体选择一个较低的级别:

with recursive cte as (
    select parent_id, child_id, level, entity
    from access_level t
    where not exists (
        select from access_level l
        where l.child_id = t.parent_id)
union all
    select t.parent_id, t.child_id, least(c.level, t.level), t.entity
    from cte c
    join access_level t
    on t.parent_id = c.child_id and t.entity = c.entity
)
select *
from cte
order by entity, parent_id

Db<>fiddle.