Zodra je je trigger hebt aangepast om alle drie de bewerkingen te dekken,
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
Een ander alternatief zijn drie afzonderlijke triggers, één voor elke actie.
Wees echter op uw hoede voor MERGE als u het gebruikt... Of wees erop voorbereid wanneer u overstapt naar SQL Server 2008 of hoger.
BEWERKEN
Ik denk dat je misschien op zoek bent naar een INSTEAD OF
trigger in plaats daarvan (hoe ironisch). Hier is een voorbeeld. Laten we eens kijken naar een heel eenvoudige tabel met een PK-kolom en een unieke kolom:
CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO
En een eenvoudige logtabel om activiteit vast te leggen:
CREATE TABLE dbo.myLog
(
foobar_id INT,
oldValue XML,
newValue XML,
[action] CHAR(6),
success BIT
);
GO
De volgende INSTEAD OF
trigger onderschept INSERT/UPDATE/DELETE
commando's, proberen om het werk dat ze zouden hebben gedaan te repliceren, en loggen of het een mislukking of succes was:
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
Laten we een paar heel eenvoudige, impliciete transactieverklaringen proberen:
-- 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
Controleer het logboek:
SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;
Resultaten:
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
Natuurlijk wilt u waarschijnlijk andere kolommen in de logtabel, zoals gebruiker, datum/tijd, misschien zelfs de originele verklaring. Dit was niet bedoeld als een volledig uitgebreide auditoplossing, maar als voorbeeld.
Zoals Mikael aangeeft, is dit afhankelijk van het feit dat de buitenste batch een enkele opdracht is die een impliciete transactie start. Het gedrag zal moeten worden getest als de buitenste batch een expliciete transactie met meerdere verklaringen is.
Merk ook op dat dit geen "mislukking" vastlegt in het geval dat bijvoorbeeld een UPDATE nul rijen beïnvloedt. U moet dus expliciet definiëren wat "mislukking" betekent - in sommige gevallen moet u mogelijk uw eigen foutafhandeling in de buitenste code bouwen, niet in een trigger.