web-dev-qa-db-ja.com

データベーストリガーの再帰を防ぐにはどうすればよいですか?

SQL Server 2008データベースのテーブルに次のトリガーがあります。繰り返し発生しているので、止める必要があります。

レコードを挿入または更新した後、そのテーブルの単一のフィールドを単純に更新しようとしています。

これがトリガーです:

ALTER TRIGGER [dbo].[tblMediaAfterInsertOrUpdate] 
   ON  [dbo].[tblMedia]
   BEFORE INSERT, UPDATE
AS 
BEGIN
    SET NOCOUNT ON

    DECLARE @IdMedia INTEGER,
        @NewSubject NVARCHAR(200)   

    SELECT @IdMedia = IdMedia, @NewSubject = Title
    FROM INSERTED

    -- Now update the unique subject field.
    -- NOTE: dbo.CreateUniqueSubject is my own function. 
    --       It just does some string manipulation.
    UPDATE tblMedia
    SET UniqueTitle = dbo.CreateUniqueSubject(@NewSubject) + 
                      CAST((IdMedia) AS VARCHAR(10))
    WHERE tblMedia.IdMedia = @IdMedia
END

誰かがトリガーの挿入が別のトリガーを再び開始するのを防ぐ方法を教えてもらえますか?

25
Pure.Krome

これがOPの質問に関連しているかどうかはわかりませんが、トリガーで再帰または相互再帰が発生しないようにする方法を見つけるためにここに来た場合は、次のようにテストできます。

IF TRIGGER_NESTLEVEL() <= 1/*this update is not coming from some other trigger*/

MSDN リンク

51
Anssssss

私は3つの可能性を考えています。

  1. トリガーの再帰を無効にする

    これにより、別のトリガーを呼び出すためにトリガーされたトリガーや、それ自体を再度呼び出すことが防止されます。これを行うには、次のコマンドを実行します。

    _ALTER DATABASE MyDataBase SET RECURSIVE_TRIGGERS OFF
    GO
    _
  2. INSTEAD OF UPDATE、INSERTのトリガーを使用

    _INSTEAD OF_トリガーを使用すると、更新/挿入される列を制御でき、コマンドを呼び出す前に置換することもできます。

  3. IF UPDATEを使用しないようにしてトリガーを制御します

    列をテストすると、トリガーがそれ自体を呼び出す場合、妥当な精度で通知されます。これを行うには、次のようにIF UPDATE()句を使用します。

    _ALTER TRIGGER [dbo].[tblMediaAfterInsertOrUpdate]
       ON  [dbo].[tblMedia]
       FOR INSERT, UPDATE
    AS
    BEGIN
        SET NOCOUNT ON
        DECLARE @IdMedia INTEGER,
            @NewSubject NVARCHAR(200)   
    
        IF UPDATE(UniqueTitle)
          RETURN;
    
        -- What is the new subject being inserted?
        SELECT @IdMedia = IdMedia, @NewSubject = Title
        FROM INSERTED
    
        -- Now update the unique subject field.
        -- NOTE: dbo.CreateUniqueSubject is my own function. 
        --       It just does some string manipulation.
        UPDATE tblMedia
        SET UniqueTitle = dbo.CreateUniqueSubject(@NewSubject) + 
                          CAST((IdMedia) AS VARCHAR(10))
        WHERE tblMedia.IdMedia = @IdMedia
    END
    _
33
Rodrigo
ALTER DATABASE <dbname> SET RECURSIVE_TRIGGERS OFF

RECURSIVE_TRIGGERS {オン|オフ}

[〜#〜] on [〜#〜]AFTERトリガーの再帰的な起動が許可されます。

[〜#〜] off [〜#〜]AFTERトリガーの直接の再帰的な起動のみが許可されていません。 AFTERトリガーの間接再帰も無効にするには、sp_configureを使用して、入れ子になったトリガーサーバーオプションを0に設定します。

RECURSIVE_TRIGGERSがOFFに設定されている場合、直接再帰のみが防止されます。間接再帰を無効にするには、ネストされたトリガーのサーバーオプションを0に設定する必要もあります。

このオプションのステータスは、sys.databasesカタログビューのis_recursive_triggers_on列、またはDATABASEPROPERTYEX関数のIsRecursiveTriggersEnabledプロパティを調べることで確認できます。

9
Remus Rusanu

私はそれを得たと思います:)

タイトルが「更新」(読み取り:挿入または更新)されたら、一意の件名を更新します。トリガーが2回目に実行されると、uniquesubjectフィールドが更新されるため、停止してトリガーから離れます。

また、変更された複数の行を処理するようにしました->これはトリガーで常に忘れます。

ALTER TRIGGER [dbo].[tblMediaAfterInsert] 
   ON  [dbo].[tblMedia]
   FOR INSERT, UPDATE
AS 
BEGIN
    SET NOCOUNT ON

    -- If the Title is getting inserted OR updated then update the unique subject.
    IF UPDATE(Title) BEGIN
        -- Now update all the unique subject fields that have been inserted or updated.
        UPDATE tblMedia 
        SET UniqueTitle = dbo.CreateUniqueSubject(b.Title) + 
                          CAST((b.IdMedia) AS VARCHAR(10))
        FROM tblMedia a
            INNER JOIN INSERTED b on a.IdMedia = b.IdMedia
    END
END
5
Pure.Krome

TRIGGER_NESTLEVELは特定のトリガーの再帰を防ぐために使用できますが、トリガーのオブジェクトIDを関数に渡すことが重要です。それ以外の場合は、別のトリガーによって挿入または更新が行われたときにトリガーが起動しないようにすることもできます。

   IF TRIGGER_NESTLEVEL(OBJECT_ID('dbo.mytrigger')) > 1
         BEGIN
             PRINT 'mytrigger exiting because TRIGGER_NESTLEVEL > 1 ';
             RETURN;
     END;

から [〜#〜] msdn [〜#〜]

パラメーターが指定されていない場合、TRIGGER_NESTLEVELは、呼び出しスタックのトリガーの総数を返します。これにはそれ自体が含まれます。

参照: 再帰トリガーの回避

5
Colin

UniqueTitleが設定されているかどうかを示す個別のNULLABLE列を持つことができます。

トリガーでtrueに設定し、「INSERTED」で値がtrueの場合、トリガーは何もしない

1
DVK

完全を期すために、いくつか追加します。 1回だけ実行する特定のafterトリガーがある場合は、sp_settriggerorderを使用して最後に実行するように設定できます。

また、再帰を実行しているトリガーを1つのトリガーに組み合わせるのが最善ではないかどうかも検討します。

0
HLGEM