Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Trigger in SQL Server – Holen Sie sich den Transaktionstyp für die Überwachungstabelle

Sobald Sie Ihren Auslöser so eingerichtet haben, dass er alle drei Operationen abdeckt,

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

Eine weitere Alternative sind drei separate Auslöser, einer für jede Aktion.

Seien Sie jedoch vorsichtig mit MERGE, wenn Sie es verwenden ... Oder seien Sie darauf vorbereitet, wenn Sie zu SQL Server 2008 oder höher wechseln.

BEARBEITEN

Ich denke, was Sie suchen, ist ein INSTEAD OF stattdessen auslösen (wie ironisch). Hier ist ein Beispiel. Betrachten wir eine sehr einfache Tabelle mit einer PK-Spalte und einer eindeutigen Spalte:

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

Und eine einfache Protokolltabelle zum Erfassen von Aktivitäten:

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

Der folgende INSTEAD OF Trigger wird INSERT/UPDATE/DELETE abfangen Befehle, versuchen Sie, die Arbeit zu replizieren, die sie getan hätten, und protokollieren Sie, ob es ein Fehler oder ein Erfolg war:

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

Lassen Sie uns einige sehr einfache, implizite Transaktionsanweisungen ausprobieren:

-- 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

Überprüfen Sie das Protokoll:

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

Ergebnisse:

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

Natürlich möchten Sie wahrscheinlich auch andere Spalten in der Protokolltabelle, wie z. B. Benutzer, Datum/Uhrzeit, vielleicht sogar die ursprüngliche Anweisung. Dies sollte keine vollständig umfassende Prüfungslösung sein, sondern nur ein Beispiel.

Wie Mikael betont, beruht dies auf der Tatsache, dass der äußere Stapel ein einzelner Befehl ist, der eine implizite Transaktion startet. Das Verhalten muss getestet werden, wenn der äußere Stapel eine explizite Transaktion mit mehreren Anweisungen ist.

Beachten Sie auch, dass dies keinen "Fehler" erfasst, wenn beispielsweise ein UPDATE null Zeilen betrifft. Sie müssen also explizit definieren, was "Fehler" bedeutet - in einigen Fällen müssen Sie möglicherweise Ihre eigene Fehlerbehandlung im äußeren Code erstellen, nicht in einem Trigger.