【发布时间】:2018-05-22 23:47:40
【问题描述】:
我的数据库中存储了一组依赖项。我正在寻找所有直接或间接依赖于当前对象的对象。由于对象可以依赖零个或多个其他对象,因此对象 1 被对象 9 依赖两次是完全合理的(9 依赖于 4 和 5,两者都依赖于 1)。我想获取依赖于当前对象的所有对象的列表而不重复。
如果有循环,这会变得更加复杂。如果没有循环,可以使用 DISTINCT,尽管不止一次地通过长链来最终剔除它们仍然是一个问题。但是,对于循环,重要的是 RECURSIVE CTE 不会与它已经看到的东西联合。
所以我到目前为止的样子是这样的:
WITH RECURSIVE __dependents AS (
SELECT object, array[object.id] AS seen_objects
FROM immediate_object_dependents(_objectid) object
UNION ALL
SELECT object, d.seen_objects || object.id
FROM __dependents d
JOIN immediate_object_dependents((d.object).id) object
ON object.id <> ALL (d.seen_objects)
) SELECT (object).* FROM __dependents;
(它在存储过程中,所以我可以传入_objectid)
不幸的是,当我之前在当前链中看到它时,这只是省略了一个给定的对象,如果递归 CTE 是深度优先完成的,这会很好,但是当它是广度优先时,就会出现问题。
理想情况下,解决方案应使用 SQL 而不是 PLPGSQL,但任何一种都可以。
作为一个例子,我在 postgres 中设置了这个:
create table objectdependencies (
id int,
dependson int
);
create index on objectdependencies (dependson);
insert into objectdependencies values (1, 2), (1, 4), (2, 3), (2, 4), (3, 4);
然后我尝试运行这个:
with recursive rdeps as (
select dep
from objectdependencies dep
where dep.dependson = 4 -- starting point
union all
select dep
from objectdependencies dep
join rdeps r
on (r.dep).id = dep.dependson
) select (dep).id from rdeps;
我期待“1、2、3”作为输出。
但是,这不知何故会永远持续下去(我也不明白)。如果我添加level 检查(select dep, 0 as level,...select dep, level + 1,on ... and level < 3),我看到 2 和 3 重复。相反,如果我添加一个看到的检查:
with recursive rdeps as (
select dep, array[id] as seen
from objectdependencies dep
where dep.dependson = 4 -- starting point
union all
select dep, r.seen || dep.id
from objectdependencies dep
join rdeps r
on (r.dep).id = dep.dependson and dep.id <> ALL (r.seen)
) select (dep).id from rdeps;
然后我得到 1、2、3、2、3,然后它就停止了。我可以在外部选择中使用DISTINCT,但这只能合理地处理这些数据,因为没有循环。有了更大的数据集和更多的循环,我们将继续增加 CTE 的输出,只是让 DISTINCT 减少它。我希望 CTE 在它已经在其他地方看到该特定值时简单地停止该分支。
编辑:这不仅仅是关于循环检测(尽管可能存在循环)。它是关于直接和间接地揭示该对象引用的所有内容。所以如果我们有 1->2->3->5->6->7 和 2->4->5,我们可以从 1 开始,到 2,从那里我们可以到 3 和 4,两者都有这些分支中的一个将转到 5,但我不需要两个分支都这样做 - 第一个可以转到 5,另一个可以简单地停在那里。然后我们继续进行 6 和 7。大多数循环检测将找不到循环并返回 5、6、7 两次。鉴于我希望我的大部分生产数据都有 0-3 个直接引用,并且其中大多数也是如此,从一个对象到另一个对象有多个分支是很常见的,并且沿着这些分支向下不会只是多余的,而且是对时间和资源的巨大浪费。
【问题讨论】:
-
如果您添加一些示例数据和预期输出会很棒。
-
所以,你的图是一个 DAG。是否保证是(单个) DAG 并且不包含循环?
-
@joop 我想我不能保证,不。可能会出问题,我不想因为它而陷入 postgres。它不应该发生,但它可能发生。不过,如果我们不将递归扩展到我们之前已经见过的对象之外,那应该不会有任何区别。
-
@Tanktalus 如果我理解正确,您正在 PostgreSQL 中寻找 CTE - CYCLE?
-
@lad2025 查看上面的 Edit 标记,它太大了,无法放入评论框中:)
标签: sql postgresql common-table-expression