sql >> Databáze >  >> RDS >> Sqlserver

Spouštěč v SQL Server - Získejte typ transakce provedené pro tabulku auditu

Jakmile nastavíte spoušť tak, aby pokrývala všechny tři operace,

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

Další alternativou jsou tři samostatné spouštěče, jeden pro každou akci.

Dejte si však pozor na MERGE, pokud jej používáte... Nebo buďte připraveni na to, až přejdete na SQL Server 2008 nebo novější.

UPRAVIT

Myslím, že to, co můžete chtít, je INSTEAD OF místo toho spoušť (jak ironické). Zde je jeden příklad. Uvažujme velmi jednoduchou tabulku se sloupcem PK a jedinečným sloupcem:

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

A jednoduchá tabulka protokolů pro zachycení aktivity:

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

Následující INSTEAD OF spouštěč zachytí INSERT/UPDATE/DELETE příkazy, pokusit se replikovat práci, kterou by udělali, a zaznamenat, zda se jednalo o selhání nebo úspěch:

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

Zkusme několik velmi jednoduchých příkazů implicitních transakcí:

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

Zkontrolujte protokol:

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

Výsledky:

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

Samozřejmě budete pravděpodobně chtít další sloupce v tabulce protokolu, jako je uživatel, datum/čas, možná i původní výpis. Toto nemělo být plně komplexní řešení auditu, jen příklad.

Jak zdůrazňuje Mikael, spoléhá se to na skutečnost, že vnější dávka je jediný příkaz, který spouští implicitní transakci. Chování bude muset být otestováno, pokud je vnější dávka explicitní transakce s více příkazy.

Všimněte si také, že to nezachycuje "selhání" v případě, kdy, řekněme, UPDATE ovlivňuje nula řádků. Takže musíte explicitně definovat, co znamená "selhání" - v některých případech možná budete muset vytvořit vlastní řešení selhání ve vnějším kódu, nikoli ve spouštěči.




  1. php/mysql nepočítá řádky v tabulce

  2. Jak zakázat PL/SQL v dotazech Oracle

  3. html ztrojnásobil svou velikost tím, že po dotazu mysql vrátil prázdné značky | jak řešit - laravel

  4. Najděte data pro každé následující finanční období rok v dotazu mdx