2009-06-13 119 views
3

(NodeId、NodeName)と構造テーブル(ParentNodeId、ChildNodeId)を持つノードテーブルがあります。 insert updateまたはdelete文が無限の関係を引き起こす可能性があるかどうかをチェックするトリガを書くにはどうすればよいですか?SQLツリーの無限ループを防ぐためのトリガー

答えて

1

親が直接的または間接的に子の子にならない循環依存条件を再帰的にチェックする必要があります。

SQL Server 2005では、同じものに対して再帰的なCTEを書くことができます。例 -

WITH [RecursiveCTE]([Id], [ParentAccountId]) AS 
(
    SELECT 
     [Id], 
     [ParentAccountId] 
    FROM [Structure] 
    WHERE [Id] = @Id 
    UNION ALL 
    SELECT 
     S.[Id], 
     S.[ParentAccountId] 
    FROM [Structure] S INNER JOIN [RecursiveCTE] RCTE ON S.[ParentAccountId] = RCTE.[Id] 
) 
SELECT * FROM [RecursiveCTE] 
+1

しかし、いったん検出されると、挿入/更新がトリガを使用して失敗する最も良い方法は何ですか? – MatBailie

+1

プラットフォームによって異なります。 Oracleでは、できるだけ多くの有益な情報をテキストに含めて、うまくいけば例外をスローします。しかし、実際は、それはアプリケーションに依存しますね。 –

+1

はい。使用しているアプリケーションによって異なります。 SQL Serverでは、例外をスローして挿入を停止します。または、代わりにトリガーを使用して何もしないでください。 – Kirtan

3

これは私のソリューションであり、これまでのところ期待通りに動作します。

CREATE TRIGGER [dbo].[CheckNodeDependence] ON [dbo].[ObjectTrees] 
AFTER INSERT 
AS 
BEGIN 
    SET NOCOUNT ON 

    DECLARE @CTable TABLE(ChildId INT NOT NULL, 
          ParentId INT NOT NULL, 
          [Level] INT NOT NULL, 
          RowId INT NOT NULL) 

    DECLARE @Level INT 
    SET @Level = 1 

    DECLARE @rows_affected INT 
    SET @rows_affected = 1 

    INSERT INTO @CTable 
    SELECT ObjectId, ParentId, 1, ObjectId FROM INSERTED 

    WHILE @rows_affected > 0 
    BEGIN 
     SET @Level = @Level + 1 
     INSERT INTO @CTable 
     SELECT T.ObjectId, T.ParentId, @Level, C.RowId 
      FROM ObjectTrees T 
       INNER JOIN @CTable C ON T.ParentId = C.ChildId 
       AND C.Level = @Level - 1 

      SET @rows_affected = @@rowcount 
      IF EXISTS(
       SELECT * FROM @CTable B 
        INNER JOIN @CTable V ON B.level = 1 
        AND V.Level > 1 
        AND V.RowId = B.RowId 
        AND V.ChildId = B.RowId) 
      BEGIN 
        DECLARE @error_message VARCHAR(200) 
        SET @error_message = 'Operation would cause illegal circular reference in tree structure, level = ' + CAST(@Level AS VARCHAR(30)) 
        RAISERROR(@error_message,16,1) 
        ROLLBACK TRANSACTION 
        RETURN 
      END 
     END 
    END 
GO 
関連する問題