2015-03-30 97 views
2

我试图使用递归查询通过结构化像这样的表找到一个路径:公用表表达式检索路径

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。 (true?)也许这是一个游标或程序代码的工作,但我想我会把它放在这里以防万一我找不到通过CTE表的路径的方法?

的参数是:

  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 

请注意,这将从1返回到0,然后从0返回到1,并重复,直到用完递归级别。

回答

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 

将选择开始的记录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被执行,...分别作为输出。

这一过程将继续添加记录到最终结果集,直到一个空集从递归构件返回时,即,直到与一个[email protected]记录已被添加到该结果集,@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

+0

谢谢,但这并不妨碍查询重复启动键。 IE,如果我的startkey是@a而我的endkey是@b,但是@a指的是@c,而@c又指向@a,我将永远不会去@b。这就是为什么我在递归成员中有NOT IN子句,但TSQL不允许它。我认为在JOIN条款中我会忽略一些显而易见的东西,这会允许这样做,但它迄今为止已经逃脱了我。 – quest4truth 2015-03-30 20:23:08

+0

@ user3481891您可以发布一些示例数据以及必要的输出结果吗? – 2015-03-30 20:27:58

+0

@ user3481891请注意,递归CTE以我试图在上面描述的方式进行操作,即每个递归只应用于先前递归级别的结果记录,而不应用于先前递归步骤期间生成的所有记录的并集。 – 2015-03-30 20:44:44