CTE 问题中的 SQL Server 递归查询

问题描述 投票:0回答:1

我有一个名为

Nodes
的表,其中包含基础文档、当前文档和目标文档:

BaseDocType . BaseDocID
DocType . DocID
TargetDocType . TargetDocID ..

我想获取任何特定节点的所有相关节点。这是我到目前为止所拥有的:

WITH CTE1 (ID, BaseDocType, BaseDocID, DocType, DocID, TargetDocType, TargetDocID) AS
(
    SELECT
        ID, BaseDocType, BaseDocID, DocType, DocID, TargetDocType, TargetDocID
    FROM
        Doc.Nodes 
    WHERE
        DocType = 8 AND DocID = 2

    UNION ALL

    SELECT
        a.ID, a.BaseDocType, a.BaseDocID, a.DocType, a.DocID, a.TargetDocType, a.TargetDocID
    FROM
        Doc.Nodes a
    INNER JOIN
        CTE1 b ON (a.BaseDocType = a.BaseDocType 
                   AND a.BaseDocID = b.BaseDocID 
                   AND a.DocType != b.DocType 
                   AND a.DocID != b.DocID)
)
SELECT *
FROM CTE1

但是查询不起作用。我收到此错误:

消息 530,第 16 级,状态 1,第 8 行
声明终止。在语句完成之前,最大递归 100 已用完。

Example

我该如何解决这个问题?

sql sql-server recursion
1个回答
0
投票

问题在于,锚行(ID = 2)找到了 ID = 1 的相关行,但这种关系也适用于另一个方向。因此,第 1 行会找到第 2 行,如此下去,无穷无尽。您需要某种递归终止条件。

我不确定为什么给出的模型具有基础/文档/目标关系而没有目标数据。

一些设置表值变量:

DECLARE @Nodes TABLE (ID INT, BaseDocType INT, BaseDocID INT, DocType INT, DocID INT, TargetDocType INT NULL, TargetDocID INT NULL);

INSERT INTO @Nodes 
VALUES 
    (1, 10, 6, 100, 2034, NULL, NULL),
    (2, 10, 6, 8, 2, NULL, NULL);

您可以通过以下方式传递这些信息,而不是在锚点中指定起始文档详细信息:

With CTE1 (ID, StartDocType, StartDocID, BaseDocType, BaseDocID, DocType, DocID, TargetDocType, TargetDocID)
As
(
    Select ID, DocType, DocID, BaseDocType, BaseDocID, DocType, DocID, TargetDocType, TargetDocID
    From @Nodes 

    Union All

    Select a.ID, b.StartDocType, b.StartDocID, a.BaseDocType, a.BaseDocID, a.DocType, a.DocID, a.TargetDocType, a.TargetDocID
    From @Nodes a
        INNER JOIN CTE1 b 
            ON (
                a.BaseDocType = b.BaseDocType 
                AND a.BaseDocID = b.BaseDocID 
                AND a.DocType != b.DocType 
                AND a.DocID != b.DocID
                AND NOT (a.DocType = b.StartDocType AND a.DocID = b.StartDocID)
                )
)
Select *
From CTE1
Where StartDocType=8 and StartDocID = 2

这很有帮助,因为递归部分可以避免重新处理起始行并从头开始循环。然而,随着数据的增加,您很可能会再次遇到同样的问题。

终止递归的最直接的方法是自己限制递归深度,通过跟踪深度并将其限制在递归部分:

With CTE1 (rlevel, ID, StartDocType, StartDocID, BaseDocType, BaseDocID, DocType, DocID, TargetDocType, TargetDocID)
As
(
    Select 1, ID, DocType, DocID, BaseDocType, BaseDocID, DocType, DocID, TargetDocType, TargetDocID
    From @Nodes

    Union All

    Select b.rlevel + 1, a.ID, b.StartDocType, b.StartDocID, a.BaseDocType, a.BaseDocID, a.DocType, a.DocID, a.TargetDocType, a.TargetDocID
    From @Nodes a
        INNER JOIN CTE1 b 
            ON (
                a.BaseDocType = b.BaseDocType 
                AND a.BaseDocID = b.BaseDocID 
                AND a.DocType != b.DocType 
                AND a.DocID != b.DocID
                )
    WHERE b.rlevel < 10
)
Select DISTINCT ID, BaseDocType, BaseDocID, DocType, DocID, TargetDocType, TargetDocID
From CTE1
Where StartDocType=8 and StartDocID = 2

可能有更多更好的方法来限制递归,但这需要更多的底层数据知识。

© www.soinside.com 2019 - 2024. All rights reserved.