公用表表达式,如何在遍历图形时避免无限递归?

时间:2010-03-05 07:23:17

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

我有一个简单的加权图

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

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

我想将图形转换为树,从A:ie开始。

  • A拥有50%的C居住地
  • 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<=1来限制递归级别会得到预期的结果,但这显然对任何实际用途都没有用。

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

1 个答案:

答案 0 :(得分:3)

您可以在另一列中构建受访节点列表,然后阻止在递归中重新访问它们。这样的事情(不确定我选择了正确的列):

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&lt; = 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.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&lt; = 1版本从此示例中获取5行,而不是上面第二个示例中的4行)。但我相信这是正确的。 b.Level&lt; = 1版本返回a-&gt; c,c-&gt; a,a-&gt; c的2级行,我认为不需要