更新时间:2023-01-17 19:20:16
这里有两种方法.第一种使用效率很低的 CTE.问题是在递归期间您无法检查结果集中的所有其他行.虽然您可以构建对给定行有贡献的行的列表,但您无法检查是否已经通过另一条路径到达该行.第二种方法使用循环一步一步地用关系填充表.这是一种比 CTE 更好的方法.
Here are two approaches. The first uses a CTE that is quite inefficient. The problem is that during recursion you cannot examine all of the other rows in the result set. While you can build a list of the rows that have contributed to a given row, you cannot check to see if you already reached that row via another path. The second approach uses a loop to populate a table with relations one step at a time. It is a much better method than the CTE.
留给读者练习:这两种方法是否会在树"中存在循环时终止,例如1 > 2 > 3 > 1?
Left as an exercise for the reader: Will the two methods terminate in the presence of a cycle in the "tree", e.g. 1 > 2 > 3 > 1?
-- Sample data.
declare @RecursiveTable as Table ( Parent Int, Child Int );
insert into @RecursiveTable ( Parent, Child ) values
( 1, 2 ), ( 1, 3 ),
( 4, 3 ),
( 5, 1 ),
( 6, 1 ),
( 5, 7 ),
( 8, 9 );
select * from @RecursiveTable;
-- Walk the tree with a recursive CTE.
-- NB: This is woefully inefficient since we cannot promptly detect
-- rows that have already been processed.
declare @Start as Int = 1;
with Pairs as (
select Parent, Child, Cast( Parent as VarChar(10) ) + '/' + Cast( Child as VarChar(10) ) as Pair
from @RecursiveTable ),
Relations as (
select Parent, Child, Cast( '|' + Pair + '|' as VarChar(1024) ) as Path
from Pairs
where Parent = @Start or Child = @Start
union all
select P.Parent, P.Child, Cast( R.Path + P.Pair + '|' as VarChar(1024) )
from Relations as R inner join
Pairs as P on P.Child = R.Parent or P.Parent = R.Child or
P.Child = R.Child or P.Parent = R.Parent
where CharIndex( '|' + P.Pair + '|', R.Path ) = 0
)
-- To see how terrible this is, try: select * from Relations
select distinct Parent, Child
from Relations
order by Parent, Child;
-- Try again a loop to add relations to a working table.
declare @Relations as Table ( Parent Int, Child Int );
insert into @Relations
select Parent, Child
from @RecursiveTable
where Parent = @Start or Child = @Start;
while @@RowCount > 0
insert into @Relations
select RT.Parent, RT.Child
from @Relations as R inner join
@RecursiveTable as RT on RT.Child = R.Child or RT.Parent = R.Parent or
RT.Child = R.Parent or RT.Parent = R.Child
except
select Parent, Child
from @Relations;
select Parent, Child
from @Relations
order by Parent, Child;