2015-03-30 69 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