考虑以下简单的 DAG:
1->2->3->4
还有一个表,#bar,描述了这个(我使用的是 SQL Server 2005):
parent_id child_id
1 2
2 3
3 4
//... other edges, not connected to the subgraph above
现在想象一下,我有一些其他任意标准来选择第一个和最后一个边,即 1->2 和 3->4。我想用这些来找到我的图表的其余部分。
我可以按如下方式编写递归 CTE(我使用的是MSDN中的术语):
with foo(parent_id,child_id) as (
// anchor member that happens to select first and last edges:
select parent_id,child_id from #bar where parent_id in (1,3)
union all
// recursive member:
select #bar.* from #bar
join foo on #bar.parent_id = foo.child_id
)
select parent_id,child_id from foo
但是,这会导致边 3->4 被选择两次:
parent_id child_id
1 2
3 4
2 3
3 4 // 2nd appearance!
如何防止查询递归到已经描述的子图中?如果在查询的“递归成员”部分中,我可以引用到目前为止由递归 CTE 检索到的所有数据(并在递归成员中提供一个谓词指示,不包括已访问的节点),我就可以实现这一点。但是,我认为我只能访问递归成员的最后一次迭代返回的数据。
当有很多这样的重复时,这将无法很好地扩展。有没有办法防止这种不必要的额外递归?
请注意,我可以在语句的最后一行使用“select distinct”来获得所需的结果,但这似乎是在完成所有(重复)递归之后应用的,所以我认为这不是一个理想的解决方案。
编辑- hainstech 建议通过添加一个谓词来排除在起始集中显式递归的向下路径来停止递归,即仅递归where foo.child_id not in (1,3)
。这仅适用于上述情况,因为它很简单——所有重复的部分都从锚节点集中开始。它不能解决它们可能不是的一般情况。例如,考虑将边 1->4 和 4->5 添加到上述集合中。边缘 4->5 将被捕获两次,即使使用建议的谓词。:(