sql >> Database teknologi >  >> RDS >> Sqlserver

Trigger i SQL Server - Få den type transaktion udført for revisionstabel

Når du har rettet din trigger til at dække alle tre operationer,

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

Et andet alternativ er tre separate udløsere, en for hver handling.

Vær dog på vagt over for MERGE, hvis du bruger det... Eller vær forberedt på det, når du flytter til SQL Server 2008 eller nyere.

REDIGER

Jeg tror, ​​hvad du kan være ude efter, er en INSTEAD OF trigger i stedet for (hvor ironisk). Her er et eksempel. Lad os overveje en meget simpel tabel med en PK-kolonne og en unik kolonne:

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

Og en simpel logtabel til at fange aktivitet:

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

Følgende INSTEAD OF trigger vil opsnappe INSERT/UPDATE/DELETE kommandoer, forsøg at replikere det arbejde, de ville have udført, og log, om det var en fiasko eller succes:

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

Lad os prøve nogle meget enkle, implicit-transaktionsudsagn:

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

Tjek loggen:

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

Resultater:

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

Selvfølgelig vil du sikkert have andre kolonner i logtabellen, såsom bruger, dato/klokkeslæt, måske endda den originale erklæring. Dette var ikke ment som en fuldstændig omfattende revisionsløsning, bare et eksempel.

Som Mikael påpeger, er dette afhængigt af, at den ydre batch er en enkelt kommando, der starter en implicit transaktion. Adfærden skal testes, hvis den ydre batch er en eksplicit transaktion med flere erklæringer.

Bemærk også, at dette ikke fanger "fejl" i det tilfælde, hvor f.eks. en OPDATERING påvirker nul rækker. Så du skal udtrykkeligt definere, hvad "fejl" betyder - i nogle tilfælde skal du muligvis bygge din egen fejlhåndtering i den ydre kode, ikke i en trigger.




  1. Hvordan får jeg dette tidsstempel i det format, jeg ønsker, Oracle SQL

  2. Sammensæt streng i MYSQL

  3. Eksporter en MySQL/MariaDB-database

  4. JDBC tynd Oracle 11g