如何在线性“祖先-后代”关系中找到最后一个后代(与其他条件匹配)
Posted
技术标签:
【中文标题】如何在线性“祖先-后代”关系中找到最后一个后代(与其他条件匹配)【英文标题】:How to find the last descendant (that matches other criteria) in a linear “ancestor-descendant” relationship 【发布时间】:2013-06-21 06:35:19 【问题描述】:此问题基于以下问题,但附加要求: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
【问题讨论】:
在你的例子中,anchestor 关系是排序的(就像祖先总是有比 descandants 更小的 id)。在你的情况下总是这样吗? 我很确定第一个问题的答案是错误的;我看不出它怎么可能处理祖先的链条。 不应该有 id=5 , id_entry=b,bool_flag=True 的记录也在预期的输出中吗? 请不要使用标题来标记您的问题。请改用标记系统。 所以 ...id_entry
对于同一链中的所有条目是否应该相同?这是由外键强制执行的,所以你可以依赖它吗?如果是这样,我的查询可以简化。
【参考方案1】:
表示为边列表的图、树、链等通常是递归公用表表达式的良好用途——即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
。您应该在(id_entry, id)
上创建一个UNIQUE
约束并在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的链,您可以在RECURSIVE
查询的连接中添加WHERE bool_value
。
SQLFiddle 示例:http://sqlfiddle.com/#!12/92a64/3
【讨论】:
您好,当我执行查询时出现错误(顺便说一句:我使用的是 postgre 8.4.1):错误:当前行的帧开始未实现第 16 行:WINDOW w AS(PARTITION BY id_root ROWS当前行之间... @QStormDS 好吧,恐怕这就是您在问题中未指定您的版本所得到的:如果您不另外说,我会假设您使用的是当前版本并进行相应的测试。ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
在查询的最终版本中实际上不是必需的(我以前使用 row_number
的方式需要它)......所以你可能只需删除它就可以了。【参考方案2】:
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 的后代(直接或间接)的人。这有效地选择了设置了标志的链的最后一条记录。
【讨论】:
@QStormDS 如果您使用的是解决方案,您通常应该接受它作为带有绿色勾号的最正确答案。 meta.stackexchange.com/questions/5234/…以上是关于如何在线性“祖先-后代”关系中找到最后一个后代(与其他条件匹配)的主要内容,如果未能解决你的问题,请参考以下文章
jQuery---[jQuery筛选之::祖先,后代,同胞,过滤,判断]