3 つの操作すべてをカバーするようにトリガーを修正したら、
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
もう 1 つの方法は、アクションごとに 1 つずつ、3 つの別々のトリガーです。
ただし、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
もちろん、ユーザー、日付/時刻、場合によっては元のステートメントなど、ログ テーブルに他の列が必要になることもあります。これは完全に包括的な監査ソリューションを意図したものではなく、単なる例です。
Mikael が指摘しているように、これは外側のバッチが暗黙的なトランザクションを開始する単一のコマンドであるという事実に依存しています。外側のバッチが明示的な複数ステートメントのトランザクションである場合、動作をテストする必要があります。
また、たとえば、UPDATE がゼロ行に影響する場合、これは「失敗」をキャプチャしないことに注意してください。そのため、「失敗」の意味を明示的に定義する必要があります。場合によっては、トリガーではなく、外部コードで独自の失敗処理を構築する必要があります。