sql >> Base de Datos >  >> RDS >> Sqlserver

Desencadenador en SQL Server:obtenga el tipo de transacción realizada para la tabla de auditoría

Una vez que arregle su disparador para cubrir las tres operaciones,

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

Otra alternativa son tres disparadores separados, uno para cada acción.

Sin embargo, tenga cuidado con MERGE si lo está utilizando... O prepárese para cuando cambie a SQL Server 2008 o posterior.

EDITAR

Creo que lo que puede estar buscando es un INSTEAD OF gatillo en su lugar (qué irónico). Aquí hay un ejemplo. Consideremos una tabla muy simple con una columna PK y una columna única:

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

Y una tabla de registro simple para capturar la actividad:

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

El siguiente INSTEAD OF el disparador interceptará INSERT/UPDATE/DELETE comandos, intente replicar el trabajo que habrían hecho y registre si fue un error o un éxito:

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

Probemos algunas instrucciones de transacciones implícitas muy 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

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

Por supuesto, probablemente desee otras columnas en la tabla de registro, como usuario, fecha/hora, tal vez incluso la declaración original. Esto no pretendía ser una solución de auditoría completa, solo un ejemplo.

Como señala Mikael, esto se basa en el hecho de que el lote externo es un único comando que inicia una transacción implícita. El comportamiento deberá probarse si el lote externo es una transacción explícita de varias instrucciones.

También tenga en cuenta que esto no captura el "fallo" en el caso de que, por ejemplo, una ACTUALIZACIÓN afecte a cero filas. Por lo tanto, debe definir explícitamente qué significa "falla"; en algunos casos, es posible que deba crear su propio manejo de fallas en el código externo, no en un disparador.