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.