Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Trigger no SQL Server - Obtenha o tipo de transação feito para a tabela de auditoria


Depois de corrigir seu gatilho para cobrir todas as três operações,
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

Outra alternativa são três gatilhos separados, um para cada ação.

No entanto, tenha cuidado com o MERGE se você o estiver usando... Ou esteja preparado para isso quando mudar para o SQL Server 2008 ou posterior.

EDITAR

Acho que o que você pode estar procurando é um INSTEAD OF gatilho em vez disso (que irônico). Aqui está um exemplo. Vamos considerar uma tabela muito simples com uma coluna PK e uma coluna exclusiva:
CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO

E uma tabela de log simples para capturar a atividade:
CREATE TABLE dbo.myLog
(
    foobar_id INT, 
    oldValue  XML, 
    newValue  XML, 
    [action]  CHAR(6), 
    success   BIT
);
GO

O seguinte INSTEAD OF o gatilho interceptará INSERT/UPDATE/DELETE comandos, tente replicar o trabalho que eles teriam feito e registre se foi uma falha ou sucesso:
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

Vamos tentar algumas instruções de transação implícita muito simples:
-- 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

Confira o registro:
SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;

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

Claro que você provavelmente quer outras colunas na tabela de log, como usuário, data/hora, talvez até a declaração original. Isso não era para ser uma solução de auditoria totalmente abrangente, apenas um exemplo.

Como aponta Mikael, isso depende do fato de que o lote externo é um único comando que inicia uma transação implícita. O comportamento terá que ser testado se o lote externo for uma transação explícita com várias instruções.

Observe também que isso não captura "falha" no caso em que, digamos, um UPDATE afeta zero linhas. Portanto, você precisa definir explicitamente o que "falha" significa - em alguns casos, pode ser necessário criar seu próprio tratamento de falhas no código externo, não em um gatilho.