Når du har rettet din trigger til at dække alle tre operationer,
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
Et andet alternativ er tre separate udløsere, en for hver handling.
Vær dog på vagt over for MERGE, hvis du bruger det... Eller vær forberedt på det, når du flytter til SQL Server 2008 eller nyere.
REDIGER
Jeg tror, hvad du kan være ude efter, er en INSTEAD OF
trigger i stedet for (hvor ironisk). Her er et eksempel. Lad os overveje en meget simpel tabel med en PK-kolonne og en unik kolonne:
CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO
Og en simpel logtabel til at fange aktivitet:
CREATE TABLE dbo.myLog
(
foobar_id INT,
oldValue XML,
newValue XML,
[action] CHAR(6),
success BIT
);
GO
Følgende INSTEAD OF
trigger vil opsnappe INSERT/UPDATE/DELETE
kommandoer, forsøg at replikere det arbejde, de ville have udført, og log, om det var en fiasko eller succes:
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
Lad os prøve nogle meget enkle, implicit-transaktionsudsagn:
-- 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
Tjek loggen:
SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;
Resultater:
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
Selvfølgelig vil du sikkert have andre kolonner i logtabellen, såsom bruger, dato/klokkeslæt, måske endda den originale erklæring. Dette var ikke ment som en fuldstændig omfattende revisionsløsning, bare et eksempel.
Som Mikael påpeger, er dette afhængigt af, at den ydre batch er en enkelt kommando, der starter en implicit transaktion. Adfærden skal testes, hvis den ydre batch er en eksplicit transaktion med flere erklæringer.
Bemærk også, at dette ikke fanger "fejl" i det tilfælde, hvor f.eks. en OPDATERING påvirker nul rækker. Så du skal udtrykkeligt definere, hvad "fejl" betyder - i nogle tilfælde skal du muligvis bygge din egen fejlhåndtering i den ydre kode, ikke i en trigger.