我正在编写触发器,用于将记录插入我的Audit
表。
每当我的目标表中的数据发生变化时,触发器将更新旧值,新值到审计表
此外,还有名为Transaction
和Transaction_Status
Transaction
列定义了事务的类型。可以是INSERT
,UPDATE
或DELETE
Transaction_Status
列表示SUCCESS
或FAILURE
如何实现这一目标?
我的触发器:
Alter Trigger TR_test
ON subscribers
FOR UPDATE
AS BEGIN
DECLARE @OldValue xml,@NewValue xml, @changedby varchar(50), @ReferenceId int
-----------------------------------------------------------------------------
SELECT @OldValue=b.username, @NewValue=a.username,
@ReferenceId = a.user_id, @changedby = a.modified_by
FROM inserted a, deleted b;
-----------------------------------------------------------------------------
INSERT INTO [dbo].[audit_log]
([old_value],[new_value],[module],[reference_id],[transaction]
,[transaction_status],[stack_trace],[modified_on],[modified_by])
VALUES
(@OldValue,@NewValue,'Subscriber',@ReferenceId,'_transaction',
'_transaction_status','_stack_trace',getdate(),555)
-----------------------------------------------------------------------------
END
答案 0 :(得分:8)
修复扳机以涵盖所有三项操作后,
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
当然,您可能需要日志表上的其他列,例如用户,日期/时间,甚至原始语句。这并不是一个完全全面的审计解决方案,只是一个例子。
正如Mikael所指出的,这依赖于外部批处理是一个启动隐式事务的单个命令。如果外部批处理是显式的多语句事务,则必须测试该行为。
另请注意,在UPDATE影响零行的情况下,这不会捕获“失败”。因此,您需要明确定义“失败”的含义 - 在某些情况下,您可能需要在外部代码中构建自己的失败处理,而不是在触发器中。