2012-12-12 12 views
5

Mam jednostki zależne (a) w mojej bazie danych, do których odwołuję się od innej jednostki (b), i biorąc pod uwagę określony (b) podmiot, potrzebuję uzyskać wszystkie (a) potrzebne jednostki. Jest to wiele do wielu mapowań, więc mam oddzielny stół mapowania. Myślę, że rekursywny Select with a CTE jest moim najlepszym wyborem, ale pojawia się problem:T-SQL Recursive Select Circular dependency

This Fiddle ilustruje mój problem. Jeśli jakiś użytkownik wprowadzi odwołanie cykliczne, mój rekursywny wybór zgrzyta do piskliwego zatrzymania. Próbowałem znaleźć jakiś sposób, żeby to naprawić. Należy zauważyć, że chociaż wprowadziłem klucze obce w skrzypcach, klucze obce nie są w rzeczywistości honorowane przez system, z którego korzystam (długotrwały argument z DBA) - wprowadziłem je, aby przepływ danych był wyraźniejszy.

Zapytanie rekurencyjne dla tych, którzy nie chcą klikać na skrzypcach:

WITH recur(objID) AS (
    SELECT usesObjID 
     FROM #otherObj 
     WHERE otherObjID = 1 
    UNION ALL 
    SELECT slaveObjID 
     FROM #objMap 
      INNER JOIN recur 
       on #objMap.masterObjID = recur.objID 
)SELECT objID from recur 

Wszelkie pomysły tam? Ten projekt nie jest produkowany, więc mogę nieco zmienić schemat, ale nie chciałbym polegać na wykrywaniu odwołań cyklicznych po wstawieniu, chyba że można to zrobić za pomocą T-SQL.

+0

nigdy nie użył tego FXN, ale nie po prostu powiedzieć 'na objMap.masterObjID = recur.objID i recur.objID <> # otherobj.usesObjID'? czy jest to poza zakresem? – Beth

Odpowiedz

8

Możliwe jest ustawienie MAXRECURSION CTE, co zapobiegnie nieskończonej pętli, ale nadal będziesz otrzymywać dziwne wyniki, ponieważ zapytanie będzie kontynuowane w pętli, dopóki nie zostanie osiągnięta maksymalna rekurencja.

Wyzwanie polega na tym, że pętla obejmuje wiele kroków, więc nie można po prostu sprawdzić bezpośredniego rodzica dziecka w celu ustalenia, czy znajduje się w pętli.

Jednym ze sposobów poradzenia sobie z tym byłoby dodanie dodatkowej kolumny do CTE ... ta nowa kolumna, tree, śledzi wszystkie identyfikatory, które zostały do ​​tej pory dołączone, i zatrzymuje się, gdy identyfikator się powtarza.

WITH recur(objID, Tree) AS (
    SELECT 
     usesObjID, 
     CAST(',' + CAST(usesObjID AS VARCHAR) + ',' AS VARCHAR) AS Tree 
    FROM otherObj 
    WHERE otherObjID = 1 
    UNION ALL 
    SELECT 
     slaveObjID, 
     CAST(recur.Tree + CAST(slaveObjID AS VARCHAR) + ',' AS VARCHAR) AS Tree 
    FROM objMap 
     INNER JOIN recur 
      ON objMap.masterObjID = recur.objID 
    WHERE recur.Tree NOT LIKE '%,' + CAST(slaveObjID AS VARCHAR) + ',%' 
)SELECT objID from recur 

Sql Fiddle Link

+0

To jest świetne! Czy myślisz, że to zadziała również, jeśli jest to wiele do wielu map od innego OJJ do Obj? (Konieczność innego tabele mapowania) – FrankieTheKneeMan

+0

@FrankieTheKneeMan Myślę, że będzie również działać. Zasadniczo wciąż śledzisz identyfikatory, do których do tej pory dołączyłeś, i zatrzymujesz się, gdy zobaczysz duplikat. –

+0

http://sqlfiddle.com/#!3/c1e62/3 <- Nie ufam Użytkownikom. Jeśli całkowicie zbrukują strukturę, mogę uzyskać ten wynik. Mógłbym użyć odrębnego, by uzyskać różne wartości (i tak zacząć), ale czy istnieje lepszy sposób na zrobienie tego? – FrankieTheKneeMan