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.