0

次のトリガーを使用して、挿入/削除されたテーブルの出力を確認しています。列を確認した後、傍受された UPDATE コマンドをサーバーに渡すにはどうすればよいですか?

CREATE TRIGGER Test1_LastUpdate ON Test1
INSTEAD OF UPDATE 
AS
    SELECT * FROM Inserted
    SELECT * FROM Deleted
GO

編集:スキーマの更新後に変更する必要のないソリューションを探しています。

CREATE TRIGGER Test1_LastUpdate2 ON Test1
INSTEAD OF UPDATE
AS
    --COMMIT UPDATE THAT WAS INTERCEPTED
END
4

3 に答える 3

2

「列を検証した後、傍受された UPDATE コマンドをサーバーに渡す」唯一の方法は、UPDATE自分で実行することです。

オプション 1 - ロールバック

ただし、これらの列がテーブルに追加されるときに、トリガーにさらに列を追加する必要はないと言いました。したがって、無効な変更を単純にロールバックする代替オプションがあります。それは次のようになります。

CREATE TRIGGER TR_Sample_U ON dbo.Sample -- No AFTER trigger needed here!
AS
IF EXISTS ( --check for disallowed modifications
   SELECT *
   FROM
      Inserted I
      INNER JOIN Deleted D
         ON I.SampleID = D.SampleID
   WHERE
      I.Something <> D.Something
      AND I.UpdateDate = D.UpdateDate
)
ROLLBACK TRAN;

オプション 2 - トリガーで UPDATE を実行する

ただし、コミットする前に値を変更する必要があるなど、更新に実際に伴う内容をより詳細に制御する必要がある場合は、自分で更新を実行する必要があります。例えば:

CREATE TRIGGER TR_Sample_U ON dbo.Sample
INSTEAD OF UPDATE
AS
SET NOCOUNT ON;
SET XACT_ABORT ON;

UPDATE S
SET S.Value = I.Value + '+'
FROM
   dbo.Sample S
   INNER JOIN Inserted I
      ON S.SampleID = I.SampleID
;

これはチェックを行わない簡単な例ですが、アイデアは得られます。Sampleテーブルで更新を実行すると、値が余分な+文字を取得することがわかります。更新がインターセプトされ、Inserted値 (更新後に提案された変更) がコミットされる前に変更されました。

実際の SQL Fiddle Demoを参照してください。

注意すべき唯一のことは、再帰です。

  1. 直接再帰

    更新により、同じベース テーブルを変更する他のトリガーが実行される可能性がある場合、最大ネスト レベルに達し、トランザクション全体がロールバックされるまで、それらの間でピンポンを取得できます。そのため、トリガー間のピンポンの可能性に注意してください。

  2. 間接再帰

    SQL Server ではデータベース レベルのRECURSIVE TRIGGERSオプションが既定でオフになっているため、おそらくこれについて心配する必要はありません。ただし、オンの場合は、新しい更新に基づいて同じトリガーを起動できます。

これらは、さまざまな方法で改善されます。

  • トリガーの内部をチェックTRIGGER_NESTLEVELし、すでに十分にネストされている場合はトリガーを終了します。

  • 直接再帰のみを回避するには、トリガーを組み合わせます。

  • 特定のケースでは、最初/最後に実行するトリガーを戦略的に割り当てることで、問題が解決する場合があります。絶対的な順序を指定することはできませんが、最初のものと最後のものを選択できます。

ピンポン問題は、独自のベース テーブルを変更する、または最終的に発生する別のテーブル (別のテーブルを変更するトリガーを含む) を介した一連の更新に関与INSTEAD OFする任意のタイプのトリガーに適用されることに注意してください。AFTER元のテーブルを変更します。

オプション 2B - AFTER UPDATE トリガーを前処理します。

私はこのオプションを 2B と呼んでいます。これは実際にはオプション 2 ですが、拡張機能を備えているためです。テーブルに列を追加するたびにトリガーを手動で更新する必要がない場合 (私も完全に同意します)、これを自動化できます。必要なすべての検証を監視する適切なトリガーを作成できるストアド プロシージャを作成します。この検証の基本コードをテーブルに配置し、SP でそれを変数に選択し、INFORMATION_SCHEMA.COLUMNSビュー内の情報をマイニングして最終更新の列を更新する SQL スクリプトを追加し、最後にトリガーを書き直すことができます。さらに、これを DDL トリガーにアタッチして、100% 自動化することもできます。ベース テーブルに列を追加または削除すると、DDL トリガーが起動し、DML トリガーが書き換えられます。

これは大変な作業のように思えますが、データ駆動型になるように設計すると、データベース全体の任意のテーブルで動作するように一般化できるため、使用シナリオによっては非常に価値があります。

于 2013-04-09T17:17:56.727 に答える
0

うーんこれも一つの選択肢…

/*
        Create  Table ExampleTable (LastRefreshed DateTime)
        Go
        Insert  ExampleTable
        Select  GetDate()
*/

Begin   Tran

If      Object_ID('tempdb..#check') Is Not Null Drop Table #check
Create  Table #check (InsertedVal DateTime, DeletedVal DateTime)

Update  ExampleTable
Set     LastRefreshed = GetDate()
Output  Inserted.LastRefreshed As InsertedVal, Deleted.LastRefreshed As DeletedVal Into #check

Select  *
From    #check

If      Exists (Select  1
                From    #check
                Where   InsertedVal > DeletedVal)
Begin
        Rollback Tran
End
Else
Begin
        Commit Tran
End

これにより、DateTime レコードを含むテーブルが作成されます。更新はそれを「現在」に更新しようとしますが、トランザクション内で実行され、挿入および削除されたレコードを一時テーブルにダンプして操作します。更新後、テーブル データに対して必要なチェックを実行して、変更をコミットするかロールバックするかを決定できます。例として、これを常にロールバックするように書いています。

于 2013-04-09T17:05:13.093 に答える