0

テーブルにレコードを挿入するためのトリガーを書いていAuditます。

ターゲット テーブルのデータが変更されるたびに、トリガーは古い値と新しい値を監査テーブルに更新します。

さらに、と呼ばれる列がありTransactionますTransaction_Status

Transaction列は、トランザクションのタイプを定義します。INSERTUPDATEまたはですDELETE
Transaction_Status列はSUCCESS、またはFAILURE

これを達成する方法は?

私のトリガー:

Alter Trigger TR_test
ON subscribers
FOR UPDATE
AS BEGIN
DECLARE @OldValue xml,@NewValue xml, @changedby varchar(50), @ReferenceId int
-----------------------------------------------------------------------------
SELECT @OldValue=b.username, @NewValue=a.username, 
       @ReferenceId = a.user_id, @changedby = a.modified_by
FROM inserted a, deleted b;
----------------------------------------------------------------------------- 
INSERT INTO [dbo].[audit_log]
           ([old_value],[new_value],[module],[reference_id],[transaction]
           ,[transaction_status],[stack_trace],[modified_on],[modified_by])
     VALUES
(@OldValue,@NewValue,'Subscriber',@ReferenceId,'_transaction',
'_transaction_status','_stack_trace',getdate(),555)

-----------------------------------------------------------------------------
END
4

1 に答える 1

8

3 つの操作すべてをカバーするようにトリガーを修正したら、

IF EXISTS (SELECT 1 FROM inserted)
BEGIN
  IF EXISTS (SELECT 1 FROM deleted)
  BEGIN
    SET @action = 'UPDATE';
  END
  ELSE
  BEGIN
    SET @action = 'INSERT';
  END
ELSE
BEGIN
  SET @action = 'DELETE';
END

もう 1 つの方法は、アクションごとに 1 つずつ、3 つの個別のトリガーです。

MERGEただし、それを使用している場合は注意してください... または、SQL Server 2008 以降に移行するときに備えてください。

編集

代わりに、あなたが求めているのは引き金だと思いますINSTEAD OF(なんて皮肉なことでしょう)。ここに一例があります。PK 列と一意の列を持つ非常に単純なテーブルを考えてみましょう。

CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO

そして、アクティビティをキャッチするための単純なログ テーブル:

CREATE TABLE dbo.myLog
(
    foobar_id INT, 
    oldValue  XML, 
    newValue  XML, 
    [action]  CHAR(6), 
    success   BIT
);
GO

次のINSTEAD OFトリガーはINSERT/UPDATE/DELETE、コマンドをインターセプトし、コマンドが実行するはずだった作業を複製しようとし、失敗か成功かをログに記録します。

CREATE TRIGGER dbo.foobar_inst
ON dbo.foobar
INSTEAD OF INSERT, UPDATE
AS
BEGIN
  SET NOCOUNT ON;
    
  DECLARE @action  CHAR(6), @success BIT;

  SELECT @action  = 'DELETE', @success = 1;
    
  IF EXISTS (SELECT 1 FROM inserted)
  BEGIN
    IF EXISTS (SELECT 1 FROM deleted)
      SET @action = 'UPDATE';
    ELSE
      SET @action = 'INSERT';
  END
    
  BEGIN TRY
    IF @action = 'INSERT'
      INSERT dbo.foobar(id, x) SELECT id, x FROM inserted;
      
    IF @action = 'UPDATE'
      UPDATE f SET x = i.x FROM dbo.foobar AS f
        INNER JOIN inserted AS i ON f.id = i.id;
      
    IF @action = 'DELETE'
        DELETE f FROM dbo.foobar AS f
          INNER JOIN inserted AS i ON f.id = i.id;
  END TRY
  BEGIN CATCH
    ROLLBACK; -- key part here!

    SET @success = 0;
  END CATCH
          
  IF @action = 'INSERT'
    INSERT dbo.myLog SELECT i.id, NULL, 
      (SELECT * FROM inserted WHERE id = i.id FOR XML PATH),
      @action, @success FROM inserted AS i;

  IF @action = 'UPDATE'
    INSERT dbo.myLog SELECT i.id, 
      (SELECT * FROM deleted  WHERE id = i.id FOR XML PATH),
      (SELECT * FROM inserted WHERE id = i.id FOR XML PATH),
      @action, @success FROM inserted AS i;

  IF @action = 'DELETE'
    INSERT dbo.myLog SELECT d.id, 
      (SELECT * FROM deleted  WHERE id = d.id FOR XML PATH),
      NULL, @action, @success FROM deleted AS d;
END
GO

非常に単純な暗黙のトランザクション ステートメントをいくつか試してみましょう。

-- these succeed:

INSERT dbo.foobar SELECT 1, 'x';
GO
INSERT dbo.foobar SELECT 2, 'y';
GO

-- fails with PK violation:

INSERT dbo.foobar SELECT 1, 'z';
GO

-- fails with UQ violation:

UPDATE dbo.foobar SET x = 'y' WHERE id = 1;
GO

ログを確認します。

SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;

結果:

foobar_id oldValue                      newValue                      action success
--------- ----------------------------- ----------------------------- ------ -------
1         NULL                          <row><id>1</id><x>x</x></row> INSERT 1
2         NULL                          <row><id>2</id><x>y</x></row> INSERT 1
1         NULL                          <row><id>1</id><x>z</x></row> INSERT 0
1         <row><id>1</id><x>x</x></row> <row><id>1</id><x>y</x></row> UPDATE 0

もちろん、ユーザー、日付/時刻、場合によっては元のステートメントなど、ログ テーブルに他の列が必要になることもあります。これは、完全に包括的な監査ソリューションを意図したものではなく、単なる例です。

Mikael が指摘するように、これは外側のバッチが暗黙的なトランザクションを開始する単一のコマンドであるという事実に依存しています。外側のバッチが明示的な複数ステートメントのトランザクションである場合、動作をテストする必要があります。

また、たとえば、UPDATE がゼロ行に影響する場合、これは「失敗」をキャプチャしないことに注意してください。そのため、「失敗」の意味を明示的に定義する必要があります。場合によっては、トリガーではなく、外部コードで独自の失敗処理を構築する必要があります。

于 2012-06-01T02:59:43.247 に答える