1

这个问题基于以下问题,但有一个附加要求:PostgreSQL:如何在线性“祖先-后代”关系中找到最后一个后代

基本上,我需要的是一个 Postgre-SQL 语句,它在匹配附加条件的线性“祖先-后代”关系中找到最后一个后代。

例子:

这里是表“RELATIONSHIP_TABLE”的内容:

id | id_ancestor | id_entry | bool_flag
---------------------------------------
1  | null        | a        | false
2  | 1           | a        | false
3  | 2           | a        | true
4  | 3           | a        | false
5  | null        | b        | true
6  | null        | c        | false
7  | 6           | c        | false

特定层次结构中的每条记录都具有相同的“id_entry” 在此示例中存在 3 种不同的“祖先-后代”关系:

1.    1 <- 2 <- 3 <- 4
2.    5
3.    6 <- 7

Question PostgreSQL: How to find the last descendant in a linear "ancestor-descendant" 关系展示了如何找到每个关系的最后一条记录。在上面的例子中:

1.    4
2.    5
3.    7

所以,这次我需要的是“id_entry”的最后一个后代,其“bool_flag”设置为true。在上面的例子中:

1.    3
2.    5
3.    <empty result>

有谁知道解决方案?

提前致谢 :)

QStormDS

4

2 回答 2

3

表示为边列表的图形、树、链等通常是递归公用表表达式(即WITH RECURSIVE查询)的良好用途。

就像是:

WITH RECURSIVE walk(id, id_ancestor, id_entry, bool_flag, id_root, generation) AS (
  SELECT id, id_ancestor, id_entry, bool_flag, id, 0 
  FROM RELATIONSHIP_TABLE 
  WHERE id_ancestor IS NULL
  UNION ALL
  SELECT x.id, x.id_ancestor, x.id_entry, x.bool_flag, walk.id_root, walk.generation + 1
  FROM RELATIONSHIP_TABLE x INNER JOIN walk ON x.id_ancestor = walk.id
)
SELECT 
  id_entry, id_root, id
FROM (
  SELECT 
    id, id_entry, bool_flag, id_root, generation,
    max(CASE WHEN bool_flag THEN generation END ) OVER w as max_enabled_generation
  FROM walk
  WINDOW w AS (PARTITION BY id_root ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
) x
WHERE generation = max_enabled_generation;

...尽管感觉确实应该有比跟踪我们在每条道路上走了多少代更好的方法来做到这一点。

如果id_entry对树的所有成员都是通用的,则可以避免需要跟踪id_root. 您应该创建一个UNIQUE约束(id_entry, id)和一个外键约束FOREIGN KEY (id_entry, id_ancestor) REFERENCES (id_entry, id)以确保顺序一致,然后使用:

WITH RECURSIVE walk(id, id_ancestor, id_entry, bool_flag, generation) AS (
  SELECT id, id_ancestor, id_entry, bool_flag, 0
  FROM RELATIONSHIP_TABLE
  WHERE id_ancestor IS NULL
  UNION ALL
  SELECT x.id, x.id_ancestor, x.id_entry, x.bool_flag, walk.generation + 1
  FROM RELATIONSHIP_TABLE x INNER JOIN walk ON x.id_ancestor = walk.id
)
SELECT
  id_entry, id
FROM (
  SELECT
    id, id_entry, bool_flag, generation,
    max(CASE WHEN bool_flag THEN generation END ) OVER w as max_enabled_generation
  FROM walk
  WINDOW w AS (PARTITION BY id_entry ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
) x
WHERE generation = max_enabled_generation;

由于这为您提供了与根父母匹配的最终后代表,因此您现在可以使用常规WHERE子句进行过滤,只需 append AND bool_flag。如果您想排除在此过程中任何bool_flag时候设置为 false 的链,您可以添加查询的连接。WHERE bool_valueRECURSIVE

SQLFiddle 示例:http ://sqlfiddle.com/#!12/92a64/3

于 2013-06-21T07:13:08.140 回答
1
WITH RECURSIVE tail AS (
    SELECT id AS opa
            , id, bool_flag FROM boolshit
    WHERE bool_flag = True
    UNION ALL
    SELECT t.opa AS opa
    , b.id, b.bool_flag FROM boolshit b
    JOIN tail t ON b.id_ancestor = t.id
    )
SELECT *
FROM boolshit bs
WHERE bs.bool_flag = True
AND NOT EXISTS (
    SELECT * FROM tail t
    WHERE t.opa = bs.id
    AND t.id <> bs.id
    AND t.bool_flag = True
    );

说明:选择所有设置了 bool_flag 的记录,除了那些也设置了 bool_flag 的后代(直接或间接)的记录。这有效地选择了设置标志的链的最后一条记录。

于 2013-06-21T08:49:49.567 回答