När du har fixat triggern så att den täcker alla tre operationerna,
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
Ett annat alternativ är tre separata utlösare, en för varje åtgärd.
Var dock försiktig med MERGE om du använder det... Eller var beredd på det när du flyttar till SQL Server 2008 eller senare.
REDIGERA
Jag tror att det du kan vara ute efter är en INSTEAD OF
trigger istället (hur ironiskt). Här är ett exempel. Låt oss överväga en mycket enkel tabell med en PK-kolumn och en unik kolumn:
CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO
Och en enkel loggtabell för att fånga aktivitet:
CREATE TABLE dbo.myLog
(
foobar_id INT,
oldValue XML,
newValue XML,
[action] CHAR(6),
success BIT
);
GO
Följande INSTEAD OF
triggern kommer att fånga upp INSERT/UPDATE/DELETE
kommandon, försök att replikera det arbete de skulle ha gjort och logga om det var ett misslyckande eller framgång:
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
Låt oss prova några mycket enkla, implicita transaktioner:
-- 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
Kontrollera loggen:
SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;
Resultat:
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
Naturligtvis vill du antagligen ha andra kolumner i loggtabellen, såsom användare, datum/tid, kanske till och med originalsatsen. Detta var inte menat att vara en heltäckande revisionslösning, bara ett exempel.
Som Mikael påpekar är detta beroende av det faktum att den yttre batchen är ett enda kommando som startar en implicit transaktion. Beteendet måste testas om den yttre batchen är en explicit transaktion med flera uttalanden.
Observera också att detta inte fångar "fel" i de fall där, säg, en UPPDATERING påverkar noll rader. Så du måste uttryckligen definiera vad "fel" betyder - i vissa fall kan du behöva bygga din egen felhantering i den yttre koden, inte i en trigger.