首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用表变量作为中间结果的table递归?

使用表变量作为中间结果的table递归?
EN

Stack Overflow用户
提问于 2015-07-25 11:11:36
回答 2查看 2.1K关注 0票数 0

我有一个棘手的逻辑问题,涉及某种形式的递归,我需要帮助。

稍微简化一下,假设在另一个表中有一个包含IDs对(AIDBID -都是int)的表,这些行将被视为同义词(每个表中ID的顺序并不重要)。我想要做的是,对任何提供的ID返回一个(distinct)集合,该集合的所有IDs都是该ID的同义词,或者是该ID的任何同义词的同义词,等等,直到任意数量的“levels”(需要有这样一个限制来防止无限循环--即1-2、2-3、3-1)。例如,给定以下行:

代码语言:javascript
复制
1,2
1,3
1,4
5,1
2,6
2,7
3,8
3,9
4,10

然后,对于ID = 5,我希望返回1-46-10

我尝试用一个表变量调用一个基于递归表的函数来保存中间结果,并使用一个游标来迭代表变量中的记录,但是由于不清楚的原因,结果是不完整的(虽然我怀疑在向表变量中添加任何行之后,我需要找到重新初始化游标的方法)。但我怀疑整个方法都有缺陷(这就是为什么我没有给出我现有的代码),如果有人有更好的建议或尝试过类似的东西,我会很感激。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-07-26 00:54:13

这里有两种方法。第一种方法使用的CTE效率很低。问题是,在递归期间,您不能检查结果集中的所有其他行。虽然可以生成对给定行有贡献的行的列表,但无法检查是否已经通过另一条路径到达该行。第二种方法使用循环一步一步地填充带有关系的表。这是一个比CTE更好的方法。

代码语言:javascript
复制
-- 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;
票数 0
EN

Stack Overflow用户

发布于 2015-07-26 01:48:52

这是个奇怪的问题,但是试试这个:

代码语言:javascript
复制
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

您可以根据此函数的输出(该函数将包含给定值的所有已定义别名值)连接到指定的递归深度。

我相信,按照这种方法,您可以摆脱游标。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31625851

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档