开发者

Common table expressions, how to avoid infinite recusion when traversing a graph?

I have a simple weighted graph

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

Suppose this describes a family and A is the father, B is the son and C is the mother. Let's say B is studying in an university and A has bought an apartment for him. A is living with C in a house which is commonly owned, 50-50.

I want to transform the graph into a tree, starting from A: ie.

  • A owns 50% of the place C is living in
  • A owns 100% of the place B is living in
  • C owns 50% of the place A is living in

The graph and the generated tree could be more elaborate but I hope you get the more general picture.

On SQL Server 2005 I have

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);

and I'm using the following common table expression to traverse the graph, starting from '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;

This causes

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

Limiting the level of recursion by uncommenti开发者_开发问答ng the And b.Level<=1 gives the expected results, but that's obviously not very useful for any practical use.

Is there a way to reference the previous iterations so that in the above example edges (ie. the FirstVertex, SecondVertex pairs) would not be repeated?


You could build up a list of visited nodes in another column and then prevent re-visiting them in your recursion. Something like this (not sure I've picked the right columns):

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;

If you want to avoid every re-traversing an edge, rather than ever re-visiting a vertex, you'd build up your links as e.g. ':' + FirstVertex + '@' + SecondVertex + ':'. In these examples, I'm just using ':' and '@' as characters that don't appear in vertex names. (Avoiding re-traversal - closer to results with b.Level <= 1, but not quite):

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 + ':%'
)

(Note that the original b.Level <= 1 version gets 5 rows from this sample, rather than the four from my second example above). But I believe it's correct. The b.Level <= 1 version returns a Level 2 row for a->c, c->a, a->c, which I don't think we want

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜