如何在线性“祖先 - 后裔”关系中找到最后一个后代(与其他条件匹配)

时间:2013-06-21 06:35:19

标签: postgresql hierarchical-data ancestor

此问题基于以下问题,但还有其他要求:PostgreSQL: How to find the last descendant in a linear "ancestor-descendant" relationship

基本上,我需要的是一个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

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

1.    4
2.    5
3.    7

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

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

有人知道解决方案吗?

提前致谢:)

QStormDS

2 个答案:

答案 0 :(得分: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子句进行过滤,只需附加AND bool_flag即可。如果您想要排除在任何点处bool_flag设置为false的链,您可以在WHERE bool_value查询的连接中添加RECURSIVE。< / p>

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

答案 1 :(得分: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设定的后代(直接或间接)的人。这有效地选择了设置了标志的链的最后一条记录。

相关问题