2012-12-12 14 views
5

私のデータベースには、別のエンティティ(b)から参照される自己依存エンティティ(a)があり、特定の(b)エンティティを指定すると、 a)必要なエンティティ。これらは多対多のマッピングであるため、私は別々のマッピングテーブルを持っています。 CTEを使用した再帰的なSelectは最善の策だと思いますが、問題にぶつかっています:T-SQL Recursive Select循環依存性

This Fiddleが私の問題を示しています。一部のユーザーが循環参照を導入した場合、私の再帰的選択は急に停止するようになります。私はこの問題を解決する方法を見つけるために私の脳を悩ましてきました。私が使用しているシステムでは、外部キーは実際には賞賛されません(DBAとの長期にわたる議論) - データフローをより明確にするためにそれらを導入しました。

フィドルにクリックスルーしたくない人のために再帰クエリ、:そこ

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 

任意のアイデア?この設計は本番ではないので、私は幾分スキーマを変更することができますが、T-SQLで行うことができない限り、挿入時に循環参照を検出することには依存しません。

+0

はこのFXNを使用することはありませんが、あなたは単に 'objMap.masterObjID = recur.objID上とrecur.objID <>#otherobj.usesObjID'言わないだろうか?それは範囲外ですか? – Beth

答えて

8

無限ループを防止するCTEのMAXRECURSIONを設定することはできますが、最大再帰がヒットするまでクエリがループ内で実行され続けるため、結果は変わってしまいます。

難しいのは、ループに複数のステップが含まれているため、ループしているかどうかを判断するために子の直接の親を確認するだけでは不十分です。

これを処理する1つの方法は、CTEに追加の列を追加することです。この新しい列treeは、これまでに含まれていたすべてのIDを追跡し、IDが繰り返されると停止します。

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

これは素晴らしいことです! otherObjからObjへの多対多のマッピングであれば、これもうまくいくと思いますか? (別のマッピングテーブルの必要性) – FrankieTheKneeMan

+0

@FrankieTheKneeManそれも動作すると思います。基本的には、今までに参加したIDを追跡していて、重複が見つかったときに停止するだけです。 –

+0

http://sqlfiddle.com/#!3/c1e62/3 < - 私はユーザーを信頼しません。彼らが構造を完全に荒らしてしまうと、私はこの結果を得ることができます。私は別個の値を得るために別個の値を使用することができますが(それはとにかく始まります)、それを行うにはより良い方法がありますか? – FrankieTheKneeMan