【问题标题】:Common table expressions, how to avoid infinite recusion when traversing a graph?常用表表达式,遍历图时如何避免无限递归?
【发布时间】:2010-03-05 07:23:16
【问题描述】:

我有一个简单的加权图

    A
 1 / \\ 0.5
  /   \\0.5
 B     C

假设这描述了一个家庭,A 是父亲,B 是儿子,C 是母亲。假设 B 在大学学习,A 为他买了一套公寓。 A和C住在共同拥有的房子里,50-50。

我想把图变成一棵树,从A开始:即。

  • A 拥有 C 居住地 50% 的股份
  • A 拥有 B 居住地的 100%
  • C 拥有 A 居住地 50% 的股份

图表和生成的树可能更详细,但我希望你能得到更一般的图片。

在 SQL Server 2005 上我有

Drop Table #graph;
Create Table #graph
    (FirstVertex VarChar(1) Not Null, 
     SecondVertex VarChar(1) Not Null, 
     Weight float);

Insert #graph Values('A','B',1);
Insert #graph Values('A','C',0.5);
Insert #graph Values('C','A',0.5);

我正在使用以下公用表表达式来遍历图形,从“A”开始:

With GraphRecursion (FirstVertex, SecondVertex, Weight, Level)
As
(
    Select FirstVertex, SecondVertex, Weight, 0 As Level
    From #graph
    Where FirstVertex='A'

    Union all

    Select a.FirstVertex, a.SecondVertex, a.Weight, b.Level+1
    From #graph a 
    Inner Join GraphRecursion b
    On a.FirstVertex=b.SecondVertex --And b.Level<=1
)
Select * From GraphRecursion;

这导致

Msg 530, Level 16, State 1, Line 11
The statement terminated. The maximum recursion 100 has 
been exhausted before statement completion.

通过取消注释 And b.Level&lt;=1 来限制递归级别会产生预期的结果,但这显然对任何实际用途都不是很有用。

有没有办法引用之前的迭代,以便在上面的示例中边(即 FirstVertex、SecondVertex 对)不会重复?

【问题讨论】:

标签: sql sql-server-2005 recursion graph common-table-expression


【解决方案1】:

您可以在另一列中建立已访问节点的列表,然后防止在递归中重新访问它们。像这样的东西(不确定我选择了正确的列):

With GraphRecursion (FirstVertex, SecondVertex, Weight, Level,Nodes)
As
(
    Select FirstVertex, SecondVertex, Weight, 0 As Level,CONVERT(varchar(8000),':' + FirstVertex + ':' + SecondVertex + ':')
    From #graph
    Where FirstVertex='A'

    Union all

    Select a.FirstVertex, a.SecondVertex, a.Weight, b.Level+1,b.Nodes + ':' + a.SecondVertex  + ':'
    From #graph a 
    Inner Join GraphRecursion b
    On a.FirstVertex=b.SecondVertex --And b.Level<=1
    where not b.Nodes like '%:' + a.SecondVertex + ':%'
)
Select * From GraphRecursion;

如果你想避免每次重新遍历一条边,而不是重新访问一个顶点,你可以建立你的链接,例如':' + FirstVertex + '@' + SecondVertex + ':'。在这些示例中,我只是使用 ':' 和 '@' 作为不出现在顶点名称中的字符。 (避免重新遍历 - 更接近 b.Level

With GraphRecursion (FirstVertex, SecondVertex, Weight, Level,Nodes)
As
(
    Select FirstVertex, SecondVertex, Weight, 0 As Level,CONVERT(varchar(8000),':' + FirstVertex + '@' + SecondVertex + ':')
    From #graph
    Where FirstVertex='A'

    Union all

    Select a.FirstVertex, a.SecondVertex, a.Weight, b.Level+1,b.Nodes + ':' + a.FirstVertex + '@' + a.SecondVertex  + ':'
    From #graph a 
    Inner Join GraphRecursion b
    On a.FirstVertex=b.SecondVertex --And b.Level<=1
    where not b.Nodes like '%:' + a.FirstVertex + '@' + a.SecondVertex + ':%'
)

(请注意,原始 b.Level c、c->a、a->c 返回 2 级行,我认为我们不希望这样做

【讨论】:

  • 非常好!不过,较长的顶点 ID 可能会导致问题。事实上,我只想得到一个三行结果,而不是重复 A,B。太糟糕了,我只提供了这么小的图表作为示例,它并不能很好地说明问题。
  • 如果您可以添加您的预期结果并解释为什么不需要第二个 a,b(为 a-> c、c->a、a->b 添加)到您的问题,我也许可以再试一次。
  • 好吧,我编了一个故事:)
猜你喜欢
  • 2017-05-30
  • 1970-01-01
  • 2010-09-08
  • 2020-11-10
  • 2012-03-27
  • 2017-10-19
  • 2016-10-27
  • 2019-08-03
  • 2013-06-05
相关资源
最近更新 更多