使用公用表表达式避免重复递归
Posted
技术标签:
【中文标题】使用公用表表达式避免重复递归【英文标题】:Avoiding duplicate recursion with Common Table Expressions 【发布时间】:2015-03-19 21:40:36 【问题描述】:假设我有一个包含 2 列 ID 和 ParentID 的表。我的数据如下所示:
ID ParentID
1 Null
2 1
3 1
4 2
4 2
因此,要根据给定 ID 查找所有关系,我的简化查询如下所示:
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]
现在假设我的表中的数据创建了一个循环关系(4 是 2 的父级,2 是 4 的父级。暂时忘记了数据库可能应该有约束来防止这种情况,上面的递归 CTE 查询产生重复记录(其中 99 个),因为它会递归地评估 2 和 4 之间的循环关系。
ID ParentID
1 Null
2 1
3 1
4 2
2 4
2 4
假设我无法控制阻止实际数据表示该循环关系,我该如何更改我的查询以防止这种情况发生。通常我会在最终选择上放置一个不同的值,但我想要 Depth 值,这使得每条记录都不同。我也希望在 CTE 中考虑它,因为 distinct 对最终选择进行操作,并且可能效率不高。
【问题讨论】:
【参考方案1】:您可以在 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];
INNER JOIN 上的那一行 AND CHARINDEX(':' + CAST(mt.[ID] AS VARCHAR(MAX)) + ':', l.[treePath]) = 0 是路径中之前的数字被过滤掉的地方。
只需复制并粘贴示例并尝试一下。
请注意,我在 CTE 上使用 CHARINDEX 的方式可能无法很好地扩展,但它确实实现了我认为您正在寻找的东西。
【讨论】:
以上是关于使用公用表表达式避免重复递归的主要内容,如果未能解决你的问题,请参考以下文章