Sql server 使用公共表表达式避免重复递归

Sql server 使用公共表表达式避免重复递归,sql-server,sql-server-2008,common-table-expression,Sql Server,Sql Server 2008,Common Table Expression,假设我有一个包含两列ID和ParentID的表。我的数据如下所示: ID ParentID 1 Null 2 1 3 1 4 2 4 2 WITH links ([ID], [ParentID], Depth) AS ( --Get the starting link SELECT [ID], [ParentID],

假设我有一个包含两列ID和ParentID的表。我的数据如下所示:

ID           ParentID
1            Null
2            1
3            1
4            2
4            2
WITH links ([ID], [ParentID], Depth)
AS
(
    --Get the starting link
    SELECT 
        [ID],
        [ParentID],
        [Depth] = 1
    FROM
        [MyTable] 
    WHERE
        [ID] = @StartID

    UNION ALL

    --Recursively get links that are parented to links already in the CTE
    SELECT 
        mt.[ID],
        mt.[ParentID],
        [Depth] = l.[Depth] + 1
    FROM
        [MyTable] mt
    JOIN
        links l ON mt.ParentID = l.ID
    WHERE
        Depth < 99
)
SELECT 
    [Depth],
    [ID],
    [ParentID]
FROM
    [links]
因此,要查找基于给定ID的所有关系,我的简化查询如下所示:

ID           ParentID
1            Null
2            1
3            1
4            2
4            2
WITH links ([ID], [ParentID], Depth)
AS
(
    --Get the starting link
    SELECT 
        [ID],
        [ParentID],
        [Depth] = 1
    FROM
        [MyTable] 
    WHERE
        [ID] = @StartID

    UNION ALL

    --Recursively get links that are parented to links already in the CTE
    SELECT 
        mt.[ID],
        mt.[ParentID],
        [Depth] = l.[Depth] + 1
    FROM
        [MyTable] mt
    JOIN
        links l ON mt.ParentID = l.ID
    WHERE
        Depth < 99
)
SELECT 
    [Depth],
    [ID],
    [ParentID]
FROM
    [links]

假设我无法控制阻止实际数据表示这种循环关系,那么如何修改查询以防止这种情况发生呢。通常,我会在最终选择上加上一个distinct,但我需要深度值,这使每个记录都不同。我还希望在CTE中解释它,因为distinct在最终select上运行,可能没有那么有效。

您可以在CTE中创建一个树路径变量,从递归查询的顶部显示您的整个路径,然后检查树路径中是否有问题的数字,如果是,则在该点中止

USE Master;
GO
CREATE DATABASE [QueryTraining];
GO
USE [QueryTraining];
GO

CREATE TABLE [MyTable] (
    ID int,  --would normally be an INT IDENTITY
    ParentID int
    );

INSERT INTO [MyTable] (ID, ParentID) 
        VALUES (1, NULL),
               (2, 1),
               (3, 1),
               (4, 2),
               (2, 4),
               (2, 4);

DECLARE @StartID AS INTEGER;
SET @StartID = 1;

;WITH links (ID, ParentID, Depth, treePath)
AS
(
    --Get the starting link
    SELECT [ID],
           [ParentID],
           [Depth] = 1, 
           CAST(':' + CAST([ID] AS VARCHAR(MAX)) AS VARCHAR(MAX)) AS treePath
      FROM [MyTable] 
     WHERE [ID] = @StartID

    UNION ALL

    --Recursively get links that are parented to links already in the CTE
    SELECT mt.[ID],
           mt.[ParentID],
           [Depth] = l.[Depth] + 1,
           CAST(l.treePath + CAST(mt.[ID] AS VARCHAR(MAX)) + ':' AS VARCHAR(MAX)) AS treePath
      FROM [MyTable] mt
     INNER JOIN links l ON mt.ParentID = l.ID
       AND CHARINDEX(':' + CAST(mt.[ID] AS VARCHAR(MAX)) + ':', l.[treePath]) = 0
     WHERE Depth < 10
)
SELECT 
    [Depth],
    [ID],
    [ParentID],
    [treePath]
FROM
    [links];
使用Master;
去
创建数据库[查询训练];
去
使用[查询训练];
去
创建表[MyTable](
ID int,--通常是一个int标识
父ID整数
);
插入[MyTable](ID,ParentID)
值(1,NULL),
(2, 1),
(3, 1),
(4, 2),
(2, 4),
(2, 4);
将@StartID声明为整数;
设置@StartID=1;
;带链接(ID、ParentID、深度、树路径)
作为
(
--获取起始链接
选择[ID],
[ParentID],
[深度]=1,
CAST(“:”+CAST([ID]AS VARCHAR(MAX))AS VARCHAR(MAX))AS treePath
从[MyTable]
其中[ID]=@StartID
联合所有
--递归地获取链接,这些链接是CTE中已有链接的父级
选择mt.[ID],
mt[ParentID],
[深度]=l[深度]+1,
CAST(l.treePath+CAST(mt.[ID]作为VARCHAR(MAX))+':'作为VARCHAR(MAX))作为treePath
从[MyTable]mt
mt.ParentID=l.ID上的内部联接链接l
和CHARINDEX(':'+CAST(mt.[ID]AS VARCHAR(MAX))+':',l.[treePath])=0
其中深度<10
)
挑选
[深度],
[ID],
[ParentID],
[树路]
从…起
[链接];
内部连接上的线表示 和CHARINDEX(':'+CAST(mt.[ID]AS VARCHAR(MAX))+':',l.[treePath])=0 是过滤掉路径中以前的数字的位置

只需复制并粘贴示例,然后尝试一下

值得注意的是,我在CTE上使用CHARINDEX的方式可能无法很好地扩展,但它确实实现了我认为您需要的功能