След като поправите тригера си, за да покрие и трите операции,
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
Друга алтернатива са три отделни тригера, по един за всяко действие.
Внимавайте обаче с MERGE, ако го използвате... Или бъдете подготвени за него, когато преминете към SQL Server 2008 или по-нов.
РЕДАКТИРАНЕ
Мисля, че това, което може да търсите, е INSTEAD OF
задейства вместо това (колко иронично). Ето един пример. Нека разгледаме много проста таблица с PK колона и уникална колона:
CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO
И проста журнална таблица за улавяне на активност:
CREATE TABLE dbo.myLog
(
foobar_id INT,
oldValue XML,
newValue XML,
[action] CHAR(6),
success BIT
);
GO
Следното INSTEAD OF
тригерът ще прихване INSERT/UPDATE/DELETE
команди, опитайте се да възпроизведете работата, която биха свършили, и регистрирайте дали е било неуспех или успех:
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
Нека опитаме някои много прости изрази за имплицитна транзакция:
-- 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
Проверете дневника:
SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;
Резултати:
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
Разбира се, вероятно искате други колони в журналната таблица, като потребител, дата/час, може би дори оригиналното изявление. Това не беше предназначено да бъде напълно изчерпателно решение за одит, а само пример.
Както посочва Микаел, това разчита на факта, че външната партида е една команда, която стартира имплицитна транзакция. Поведението ще трябва да бъде тествано, ако външният пакет е изрична транзакция с множество изрази.
Също така имайте предвид, че това не улавя "отказ" в случай, когато, да речем, АКТУАЛИЗИРАНЕ засяга нула редове. Така че трябва изрично да дефинирате какво означава "неуспех" - в някои случаи може да се наложи да изградите свое собствено обработване на неуспех във външния код, а не в тригер.