我有一个棘手的逻辑问题,涉及某种形式的递归,我需要帮助。
稍微简化一下,假设在另一个表中有一个包含IDs对(AID和BID -都是int)的表,这些行将被视为同义词(每个表中ID的顺序并不重要)。我想要做的是,对任何提供的ID返回一个(distinct)集合,该集合的所有IDs都是该ID的同义词,或者是该ID的任何同义词的同义词,等等,直到任意数量的“levels”(需要有这样一个限制来防止无限循环--即1-2、2-3、3-1)。例如,给定以下行:
1,2
1,3
1,4
5,1
2,6
2,7
3,8
3,9
4,10然后,对于ID = 5,我希望返回1-4和6-10。
我尝试用一个表变量调用一个基于递归表的函数来保存中间结果,并使用一个游标来迭代表变量中的记录,但是由于不清楚的原因,结果是不完整的(虽然我怀疑在向表变量中添加任何行之后,我需要找到重新初始化游标的方法)。但我怀疑整个方法都有缺陷(这就是为什么我没有给出我现有的代码),如果有人有更好的建议或尝试过类似的东西,我会很感激。
发布于 2015-07-26 00:54:13
这里有两种方法。第一种方法使用的CTE效率很低。问题是,在递归期间,您不能检查结果集中的所有其他行。虽然可以生成对给定行有贡献的行的列表,但无法检查是否已经通过另一条路径到达该行。第二种方法使用循环一步一步地填充带有关系的表。这是一个比CTE更好的方法。
-- Sample data.
declare @RecursiveTable as Table ( AId Int, BId Int );
insert into @RecursiveTable ( AId, BId ) values
( 1, 2 ), ( 1, 3 ), ( 1, 4 ),
( 5, 1 ),
( 2, 6 ), ( 2, 7 ),
( 3, 8 ), ( 3, 9 ),
( 4, 10 );
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 = 5;
with Pairs as (
select AId, BId, Cast( AId as VarChar(10) ) + '/' + Cast( BId as VarChar(10) ) as Pair
from @RecursiveTable ),
Relations as (
select AId, BId, Cast( '|' + Pair + '|' as VarChar(1024) ) as Path
from Pairs
where AId = @Start or BId = @Start
union all
select P.AId, P.BId, Cast( R.Path + P.Pair + '|' as VarChar(1024) )
from Relations as R inner join
Pairs as P on P.BId = R.AId or P.AId = R.BId or
P.BId = R.BId or P.AId = R.AId
where CharIndex( '|' + P.Pair + '|', R.Path ) = 0
)
-- To see how terrible this is, try: select * from Relations
select AId as Id
from Relations
where AId <> @Start
union
select BId
from Relations
where BId <> @Start
order by Id;
-- Try again using a loop to add relations to a working table.
declare @Relations as Table ( AId Int, BId Int );
insert into @Relations
select AId, BId
from @RecursiveTable
where AId = @Start or BId = @Start;
while @@RowCount > 0
insert into @Relations
select RT.AId, RT.BId
from @Relations as R inner join
@RecursiveTable as RT on RT.BId = R.BId or RT.AId = R.AId or
RT.BId = R.AId or RT.AId = R.BId
except
select AId, BId
from @Relations;
select AId as Id
from @Relations
where AId <> @Start
union
select BId
from @Relations
where BId <> @Start
order by Id;发布于 2015-07-26 01:48:52
这是个奇怪的问题,但是试试这个:
CREATE FUNCTION fSynonyms(@val Int, @maxRecursions Int)
RETURNS @ret TABLE
(
num Int
)
AS BEGIN
DECLARE @Values TABLE ( num Int )
INSERT INTO @Values VALUES(@val)
DECLARE @i Int = 0
WHILE @i < @maxRecursions
BEGIN
INSERT INTO VAL
SELECT aid
FROM @Values VAL
INNER JOIN Aliases ALIAS
ON ALIAS.bid = VAL.num
INSERT INTO VAL
SELECT bid
FROM @Values VAL
INNER JOIN Aliases ALIAS
ON ALIAS.aid = VAL.num
SET @i += 1
END
INSERT INTO @Ret
SELECT DISTINCT num FROM @Values
RETURN
END您可以根据此函数的输出(该函数将包含给定值的所有已定义别名值)连接到指定的递归深度。
我相信,按照这种方法,您可以摆脱游标。
https://stackoverflow.com/questions/31625851
复制相似问题