sql >> データベース >  >> RDS >> Sqlserver

SQL Server のトリガー - 監査テーブルで行われたトランザクションの種類を取得します

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




    1. 複数の行を連結する

    2. データベース全体でテーブル名と特定の列の序数位置を取得する方法

    3. RDFLibを介したPostgreSQLテーブルへのRDFデータのロード-SQLAlchemy

    4. テーブルに存在しないレコードを返します