检索路径的常用 Table 表达式

Common Table Expressions to retrieve path

我正在尝试使用递归查询来查找通过 table 结构如下的路径:

相关实体

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 找到通过 table 的路径的方法?

参数为:

  1. 以开始和结束键开始
  2. 基本查询使用开始键
  3. 递归查询在包含结束键时应该停止(一直无法弄清楚)并且不应重复开始键。
  4. 可以使用 MAXRECURSION 选项在一定次数的迭代后停止。

感谢所有 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

注意这个 returns 从 1 到 0,然后从 0 到 1,并重复直到我 运行 超出递归级别。

您需要像这样修改您的查询:

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

将 select(从,到)记录链的 起始记录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

将以 T0T1、... 作为输入和 T1T2、... 分别作为输出执行。

此过程将继续向最终结果集添加记录,直到从递归成员返回一个空集为止,即直到将 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然后用于检查是否满足循环路径。

SQL Fiddle Demo here