SQL中的递归查询

时间:2015-10-09 12:04:09

标签: sql-server

我有2个表:Document和DocumentLink。 DocumentLink有两个字段:MainDocID和LinkedDocID,它们填充了Document表中的DocID值。每个DocID都可以在两个字段中。

例如:

MainDocID LinkedDocID  
317     3214  
7969    317  
317     11723  
317     17387  
7969    19325  
19325   19847  

我写了一个返回所有链接的DocID的程序。对于我的示例中的任何DocID相同的结果:

317
3214
7969
11723
17387
19325
19847

这是程序:

CREATE PROCEDURE [dbo].[GetAllLinkedDocumentsForStack](@DocID int) AS BEGIN 
    create table #doc_tree (id int IDENTITY (1, 1) NOT NULL ,
        doc_id int NULL ,
        isdone int NOT NULL DEFAULT (0)
    ) ON [PRIMARY]

    insert into #doc_tree (doc_id) values (@DocID)

    declare @id_header int 
    set @id_header = @DocID

    declare c0 cursor for select doc_id from #doc_tree where isdone = 0

    open c0

    fetch next from c0 into @id_header

    while @@fetch_status=0
    begin

        insert into #doc_tree (doc_id)
          select LinkedDocID from DocumentLink where MainDocID = @id_header and LinkedDocID not in (select doc_id from #doc_tree)
          union 
          select MainDocID from DocumentLink where LinkedDocID = @id_header and MainDocID not in (select doc_id from #doc_tree);

        update #doc_tree set isdone = 1 where doc_id = @id_header

        fetch next from c0 into @id_header

    end 

    close c0
    deallocate c0

    select DocID from Document where DocID In (select Doc_ID from #doc_tree)                            

    drop table #doc_tree END

我的问题:我如何对CTE做同样的事情?

1 个答案:

答案 0 :(得分:1)

这对CTE来说有点棘手。走过图表时的关键是找到一种方法来防止无限循环。这需要将它们填充到字符串中并检查字符串以防止无限循环的方法:

with cte as (
      select @docid as docid,
             cast(',' + cast(@docid as varchar(max)) + ',' as varchar(max)) as list
      union all
      select maindocid, cast(list + maindocid + ',' as varchar(max))
      from DocumentLink dl join
           cte
           on dl.linkeddocid = cte.docid
      where cte.list  not like '%,' + dl.maindocid + ',%'
      union all
      select linkeddocid, cast(list + linkeddocid + ',' as varchar(max))
      from DocumentLink dl join
           cte
           on dl.maindocid = cte.docid
      where cte.list  not like '%,' + dl.linkeddocid + ',%'
    )
select docid
from cte;

另请注意,有两个递归组件,一个用于在列表中单向行走。