Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Wyzwalacz w SQL Server - Uzyskaj typ transakcji wykonanej dla tabeli audytu

Po naprawieniu wyzwalacza tak, aby obejmował wszystkie trzy operacje,

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

Inną alternatywą są trzy oddzielne wyzwalacze, po jednym dla każdej akcji.

Uważaj jednak na MERGE, jeśli go używasz... Lub przygotuj się na to, gdy przejdziesz na SQL Server 2008 lub nowszy.

EDYTUJ

Myślę, że to, czego możesz szukać, to INSTEAD OF zamiast tego wyzwalacz (jak na ironię). Oto jeden przykład. Rozważmy bardzo prostą tabelę z kolumną PK i unikalną kolumną:

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

I prosta tabela dziennika do przechwytywania aktywności:

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

Następujące INSTEAD OF wyzwalacz przechwyci INSERT/UPDATE/DELETE poleceń, próbuj powtórzyć pracę, którą wykonali, i zarejestruj, czy była to porażka, czy sukces:

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

Wypróbujmy kilka bardzo prostych, niejawnych instrukcji dotyczących transakcji:

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

Sprawdź dziennik:

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

Wyniki:

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

Oczywiście prawdopodobnie potrzebujesz innych kolumn w tabeli dziennika, takich jak użytkownik, data/godzina, może nawet oryginalne oświadczenie. Nie miało to być w pełni kompleksowe rozwiązanie audytowe, tylko przykład.

Jak wskazuje Mikael, opiera się to na fakcie, że zewnętrzna partia to pojedyncze polecenie, które rozpoczyna niejawną transakcję. Zachowanie będzie musiało zostać przetestowane, jeśli zewnętrzna partia jest jawną transakcją zawierającą wiele instrukcji.

Należy również zauważyć, że nie przechwytuje to „awarii” w przypadku, gdy, powiedzmy, UPDATE wpływa na zero wierszy. Musisz więc wyraźnie zdefiniować, co oznacza "awaria" - w niektórych przypadkach może być konieczne zbudowanie własnej obsługi błędów w zewnętrznym kodzie, a nie w wyzwalaczu.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Inżynieria odwrotna do tworzenia diagramów baz danych w programie Visio z SQL Server 2008

  2. Koszt zapytania w stosunku do partii wynosi 100%

  3. Wysyłaj SMS-y za pomocą wyzwalaczy i procedur składowanych z SQL Server 2008

  4. Jak czytać kolumnę XML w SQL Server 2008?

  5. Jak połączyć te dwie rzeczy razem? Varchar guid i guid wpisują oba klucze podstawowe