我正在尝试使用递归查询来查找通过如下结构的表格的路径:
RelatedEntities
FromKey TINYINT
ToKey TINYINT
...more....
我以为我可以这样做:
DECLARE @startKey UNIQUEIDENTIFIER, @endKey UNIQUEIDENTIFIER;
SET @startKey = 0;
SET @endKey = 3;
;With findPath
AS
(
SELECT FromKey, ToKey
FROM RelatedEntities
WHERE FromKey = @startKey
UNION ALL
SELECT FromKey, ToKey
FROM RelatedEntities r
JOIN findPath b
ON r.FromKey = b.ToKey
AND r.FromKey NOT IN (SELECT FromKey FROM b)
)
SELECT * FROM findPath;
此代码失败,因为我无法在CTE中使用子查询。似乎规则是递归查询只能包含对CTE的一个引用。 (是吗?)也许这是光标或程序代码的工作,但我想我会把它放在这里,以防我找不到通过CTE表找到路径的方法?
参数是:
感谢各位CTE大师。让我直截了当。
将此值从UNIQUEIDENTIFIERS更改为TINYINT以提高可读性。 SQL结构是相同的。这是一些测试它的代码。
CREATE TABLE RelatedEntities(FromKey TINYINT, ToKey TINYINT);
INSERT RelatedEntities(FromKey, ToKey)
VALUES
(1, 0),
(0, 1),
(1, 7),
(7, 1),
(3, 4),
(4, 3)
;With FindPath
AS
(
SELECT FromKey, ToKey, 0 AS recursionLevel
FROM RelatedEntities
WHERE FromKey = 1
UNION ALL
SELECT r.FromKey, r.ToKey, recursionLevel = recursionLevel + 1
FROM RelatedEntities r
INNER JOIN FindPath b ON r.FromKey = b.ToKey
WHERE b.ToKey <> 3 AND RecursionLevel < 10
)
SELECT * FROM FindPath
ORDER BY recursionLevel
请注意,这会从1返回0,然后从0返回到1,并重复直到我用完递归级别。
答案 0 :(得分:2)
您需要像这样修改您的查询:
DECLARE @startKey UNIQUEIDENTIFIER, @endKey UNIQUEIDENTIFIER;
DECLARE @maxRecursion INT = 100
SET @startKey = '00000000-0000-0000-0000-000000000000';
SET @endKey = 'F7801327-C037-AA93-67D1-B7892F6093A7';
;With FindPath
AS
(
SELECT FromKey, ToKey, 0 AS recursionLevel
FROM RelatedEntities
WHERE FromKey = @startKey
UNION ALL
SELECT r.FromKey, r.ToKey, recursionLevel = recursionLevel +1
FROM RelatedEntities r
INNER JOIN FindPath b ON r.FromKey = b.ToKey
WHERE b.ToKey <> @endKey AND recursionLevel < @maxRecursion
)
SELECT * FROM FindPath;
以上CTE
的锚点成员:
SELECT FromKey, ToKey, 0 AS recursionLevel
FROM RelatedEntities
WHERE FromKey = @startKey
将选择(From,To)记录链的起始记录,T0
。
CTE
的递归成员:
SELECT r.FromKey, r.ToKey, recursionLevel = recursionLevel +1
FROM RelatedEntities r
INNER JOIN FindPath b ON r.FromKey = b.ToKey
WHERE b.ToKey <> @endKey AND recursionLevel < @maxRecursion
将以T0
,T1
,...作为输入执行,T1
,T2
,...分别作为输出执行。
此过程将继续向最终结果集添加记录,直到从递归成员返回空集,即直到将ToKey=@endKey
的记录添加到结果集或@maxRecursion级别。
修改强>
您可以使用以下查询来有效处理任何循环路径:
;With FindPath
AS
(
SELECT FromKey, ToKey,
0 AS recursionLevel,
CAST(FromKey AS VARCHAR(MAX)) AS FromKeys
FROM RelatedEntities
WHERE FromKey = 1
UNION ALL
SELECT r.FromKey, r.ToKey,
recursionLevel = recursionLevel + 1,
FromKeys = FromKeys + ',' + CAST(r.FromKey AS VARCHAR(MAX))
FROM RelatedEntities r
INNER JOIN FindPath b ON r.FromKey = b.ToKey
WHERE (b.ToKey <> 3)
AND (RecursionLevel < 10)
AND PATINDEX('%,' + CAST(r.ToKey AS VARCHAR(MAX)) + ',%', ',' + FromKeys + ',') = 0
)
SELECT * FROM FindPath
ORDER BY recursionLevel
计算字段FromKeys
用于将FromKey
传递到下一个递归级别。这样,来自先前递归级别的任何键都使用字符串连接从一个级别累积到另一个级别。然后使用PATINDEX
检查是否已满足循环路径。