【问题标题】:TSQL: Handle infinite loop in dataTSQL:处理数据中的无限循环
【发布时间】:2013-04-18 19:01:07
【问题描述】:

我有一个表格 FolderXDoc:

CREATE TABLE [dbo].[FolderXDoc](
[fldid] [int] NOT NULL,
[Xorder] [int] NOT NULL,
[docid] [int] NOT NULL,
CONSTRAINT [FolderXDoc$pk] PRIMARY KEY CLUSTERED 
(
[fldid] ASC,
[Xorder] ASC,
[docid] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

我的应用程序允许此表中的循环引用,因此允许以下数据:

fldid|Xorder|docid
1|1|2
2|1|3
3|1|4
4|1|2

所以文件夹 1 包含文件夹 2,其中包含文件夹 3。文件夹 3 包含文件夹 4。文件夹 4 包含文件夹 2,因此我们有一个循环(1/2/3/4/2/3/4/2/3/ 4/...)

现在我想递归检索文件夹的所有包含元素。我用 CTE 试过这个,但由于数据的循环,这不起作用。我想在检测到循环时停止递归。因此,当我检索 1 的包含元素时,我期望结果集 (2,3,4)。

我用用户定义的函数试过这个:

CREATE FUNCTION [dbo].[DocChildren](@fldid int)
RETURNS TABLE
AS
RETURN
(
     WITH n AS 
           (SELECT f.fldid, f.docid
            FROM folderxdoc f where f.fldid = @fldid
           UNION ALL

           SELECT n.fldid, nplus1.docid  
            FROM folderxdoc as nplus1, n
                WHERE n.docid = nplus1.fldid and n.docid != @fldid)

     SELECT docid FROM n
  )

该函数处理起始 id 的循环循环,但当循环发生在包含的元素中时不处理。 我该怎么做才能解决这个问题?

感谢您的帮助!

【问题讨论】:

  • 当您遇到这些无限循环之一时,您想要什么输出?你想让它显示第一个完整的链吗?在你上面的例子中,你想要 1/2/3/4,还是 1/2/3/4/2/3/4,或者别的什么?
  • 您可以找到在 CTE here 中检测循环的示例。它涉及在递归期间组装路径并测试重复条目。
  • @注册用户:感谢您的反馈。我已经编辑了这个问题。对于 1 中包含的所有元素,我的预期结果集是 (2,3,4)。
  • @Habo:感谢您的提示!我根据您提供的示例设法解决了这个问题:sqlfiddle.com/#!3/cc8b3/1

标签: sql-server tsql


【解决方案1】:

也许一个用来标记节点何时被访问过的临时表可能会有所帮助。

基本上,当您访问每个节点时,将其推送到临时表中并根据临时表检查每个节点。找到现有节点时停止。

可能需要游标来实现这一点,尽管这可能很糟糕。

【讨论】:

    【解决方案2】:

    我使用@HaBo 的提示解决了这个问题。

    我在递归期间组装了路径并检查了重复条目。您可以在此处找到生成的查询: http://sqlfiddle.com/#!3/cc8b3/1

     WITH n AS 
           (SELECT f.fldid, f.docid, ',' + cast(f.fldid as varchar(max)) + ',' levels
            FROM folderxdoc f where f.fldid = 1
           UNION ALL
    
           SELECT n.fldid, nplus1.docid,n.levels+ cast(n.docid as varchar(max)) + ',' levels
            FROM folderxdoc as nplus1, n
                WHERE n.docid = nplus1.fldid AND
                n.levels not like ('%,' + cast(nplus1.docid as varchar(max)) + ',%')
           )
    
     SELECT fldid, docid, levels FROM n
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-13
      • 2019-09-27
      • 2020-07-01
      • 2012-04-07
      相关资源
      最近更新 更多