当前位置: 首页 > 面试题库 >

防止多次递归CTE访问节点

裴楚青
2023-03-14
问题内容

考虑以下简单的DAG:

  1->2->3->4
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检索到的所有数据
(并在递归成员中提供一个谓词,表示已访问的节点除外),则可以实现此目的。但是,我认为我只能访问递归成员 的最后一次迭代 返回的数据。

当有很多这样的重复时,这将无法很好地扩展。有没有办法防止这种不必要的额外递归?

请注意,我可以在语句的最后一行使用“选择不同”来实现所需的结果,但这似乎在 完成 所有(重复)递归 之后 才应用,因此我认为这不是理想的解决方案

Edit -hainstech建议通过添加谓词来排除递归在起始集中明确存在的路径(即仅递归)的方式来停止递归where foo.child_id not in (1,3)。这仅对简单情况有效,因为它很简单-所有重复的部分都在节点的锚点集合内开始。它不能解决可能不存在的一般情况。例如,考虑将边线1->
4和4-> 5添加到上述集合中。即使使用建议的谓词,边缘4-> 5也会被捕获两次。:(


问题答案:

CTE的是递归的。

当您CTE有多个初始条件时,这意味着它们也具有不同的递归堆栈,并且无法使用另一个堆栈中的一个堆栈中的信息。

在您的示例中,递归堆栈将如下所示:

(1) - first IN condition
(1, 2)
(1, 2, 3)
(1, 2, 3, 4)
(1, 2, 3) - no more children
(1, 2) - no more children
(1) - no more children, going to second IN condition

(3) - second condition
(3, 4)
(3) - no more children, returning

如您所见,这些递归堆栈不相交。

您可能会将访问的值记录在一个临时表中,JOIN每个值都带有临时表,如果找到了该值,则不遵循该值,但是SQL Server不支持这些操作。

所以你只用SELECT DISTINCT



 类似资料:
  • 不知道你是否曾经看到过一个论坛或者博客,在一个帖子或者文章后面出现多条重复的记录,这些大多数是因为用户重复递交了留言的表单引起的。由于种种原因,用户经常会重复递交表单。通常这只是鼠标的误操作,如双击了递交按钮,也可能是为了编辑或者再次核对填写过的信息,点击了浏览器的后退按钮,然后又再次点击了递交按钮而不是浏览器的前进按钮。当然,也可能是故意的——比如,在某项在线调查或者博彩活动中重复投票。那我们如

  • 问题内容: 在这个sqlfiddle中… http://sqlfiddle.com/#!6/b6587/6 我收到以下错误…。 声明终止。在语句完成之前,最大递归100已用尽。 我知道CTE第二选择的where子句中需要进行“终止检查”。即使您取消注释WHERE子句,我也会遇到相同的错误。 我只是想了解1)为什么根本需要它……毕竟每个订单行都与每个客户行都有关系,2)由于需要“终止检查”,因此该示

  • 问题内容: 此查询生成从1到4的数字。 但是,如果我对此进行修改, 它给 错误:“ z”处或附近的语法错误 我在这里做错了什么? 问题答案: 我认为这是因为RECURSIVE是WITH语句的修饰符,而不是常用表表达式的属性,因此您可以像这样使用它:

  • 问题内容: 我正在尝试执行我认为使用CTE进行递归比较困难的事情是SQL Server 2008。 在下面的示例中,您可以假设固定深度为3 …没有任何比这更低的深度了。在现实生活中,深度是“更深的”,但仍然是固定的。在示例中,我尝试将其简化一些。 我的输入数据如下。 我的CTE的输出应为下表。 如果我可以在输出中获得ID列,则可以肯定地可以映射到查找表中的名称。 我也乐于接受其他方法来完成此任务,

  • 问题内容: 当用户单击登录或注册按钮时,我试图阻止多个请求。这是我的代码,但是不起作用。只是第一次工作正常,然后返回false。 有任何想法吗?谢谢! 问题答案: 问题在这里: 不再指向按钮。

  • 问题内容: 假设具有以下CTE,这些CTE返回我已经拥有的某些树数据(邻接模型)的级别(取自Linq中的分层数据- options和performance): 我想知道通过使用C#而不是SQL进行递归是否会提高性能。假设我有一个IQueryable,其中Tree是表示层次结构表中条目的实体,谁能向我展示如何执行CTE与递归C#函数相同的工作?类似于以下内容: 看到使用lambda表达式很容易做到这