sql >> Databasteknik >  >> RDS >> Sqlserver

Trigger i SQL Server - Få typen av transaktion gjord för revisionstabell

När du har fixat triggern så att den täcker alla tre operationerna,

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

Ett annat alternativ är tre separata utlösare, en för varje åtgärd.

Var dock försiktig med MERGE om du använder det... Eller var beredd på det när du flyttar till SQL Server 2008 eller senare.

REDIGERA

Jag tror att det du kan vara ute efter är en INSTEAD OF trigger istället (hur ironiskt). Här är ett exempel. Låt oss överväga en mycket enkel tabell med en PK-kolumn och en unik kolumn:

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

Och en enkel loggtabell för att fånga aktivitet:

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

Följande INSTEAD OF triggern kommer att fånga upp INSERT/UPDATE/DELETE kommandon, försök att replikera det arbete de skulle ha gjort och logga om det var ett misslyckande eller framgång:

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

Låt oss prova några mycket enkla, implicita transaktioner:

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

Kontrollera loggen:

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

Resultat:

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

Naturligtvis vill du antagligen ha andra kolumner i loggtabellen, såsom användare, datum/tid, kanske till och med originalsatsen. Detta var inte menat att vara en heltäckande revisionslösning, bara ett exempel.

Som Mikael påpekar är detta beroende av det faktum att den yttre batchen är ett enda kommando som startar en implicit transaktion. Beteendet måste testas om den yttre batchen är en explicit transaktion med flera uttalanden.

Observera också att detta inte fångar "fel" i de fall där, säg, en UPPDATERING påverkar noll rader. Så du måste uttryckligen definiera vad "fel" betyder - i vissa fall kan du behöva bygga din egen felhantering i den yttre koden, inte i en trigger.




  1. Hur massuppdatera mysql-data med en fråga?

  2. mysqli_stmt_get_result alternativ för php 5.2.6

  3. Utför enkel aritmetik i MySQL-sats eller i PHP-kod

  4. Hur man sprider medelvärdet mellan två intervaller i oracle