Une fois que vous avez fixé votre déclencheur pour couvrir les trois opérations,
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
Une autre alternative est trois déclencheurs distincts, un pour chaque action.
Méfiez-vous de MERGE si vous l'utilisez... Ou préparez-vous à cela lorsque vous passerez à SQL Server 2008 ou au-delà.
MODIFIER
Je pense que ce que vous recherchez est peut-être un INSTEAD OF
déclencheur à la place (quelle ironie). Voici un exemple. Considérons un tableau très simple avec une colonne PK et une colonne unique :
CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO
Et un simple tableau de journal pour capturer l'activité :
CREATE TABLE dbo.myLog
(
foobar_id INT,
oldValue XML,
newValue XML,
[action] CHAR(6),
success BIT
);
GO
Le INSTEAD OF
suivant le déclencheur interceptera INSERT/UPDATE/DELETE
commandes, essayez de reproduire le travail qu'ils auraient fait et enregistrez si c'était un échec ou un succès :
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
Essayons quelques instructions de transaction implicites très 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
Vérifiez le journal :
SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;
Résultats :
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
Bien sûr, vous voulez probablement d'autres colonnes dans la table de journal, telles que l'utilisateur, la date/l'heure, peut-être même la déclaration d'origine. Ce n'était pas censé être une solution d'audit complète, juste un exemple.
Comme le souligne Mikael, cela repose sur le fait que le lot externe est une seule commande qui démarre une transaction implicite. Le comportement devra être testé si le lot externe est une transaction explicite à plusieurs instructions.
Notez également que cela ne capture pas "l'échec" dans le cas où, par exemple, un UPDATE affecte zéro ligne. Vous devez donc définir explicitement ce que signifie "échec" - dans certains cas, vous devrez peut-être créer votre propre gestion des échecs dans le code externe, pas dans un déclencheur.