我有一个棘手的逻辑问题涉及某种形式的递归,我需要帮助。
略微简化,假设有一个表格包含另一个表中要考虑的IDs
(AID
和BID
- 两个int
行对同义词(每对中ID
s的顺序并不重要)。我想要做的是让任何提供的ID
返回(distinct
)所有ID
的{{1}}个集合,它们是ID
的同义词,或者是任何同义词的同义词ID
等等,最多可达到任意数量的'等级。 (需要有这样的限制来防止无限循环 - 即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
。
我尝试使用表变量调用基于表的递归函数来保存中间结果并使用游标迭代表变量中的记录,但结果不完整的原因不明确(尽管我怀疑我需要找到一种方法,在向表变量添加任何行后重新初始化游标)。但我怀疑整个方法存在缺陷(这就是我没有提供现有代码的原因),如果有人有更好的建议或尝试类似的东西,我将不胜感激。
答案 0 :(得分:0)
这是两种方法。第一种使用效率非常低的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;
答案 1 :(得分:0)
这是一个奇怪的问题,但试试这个:
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
您可以连接此函数的输出,该输出将包括给定值的所有已定义别名值,直到指定的递归深度。
我确信,按照这种方法,你可以摆脱光标。