Anu*_*uya 0 sql-server triggers sql-server-2005
我正在写入将记录插入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
Run Code Online (Sandbox Code Playgroud)
修复触发器以涵盖所有三个操作后,
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
Run Code Online (Sandbox Code Playgroud)
另一种选择是三个独立的触发器,每个触发器一个.
如果您正在使用它,请注意MERGE ......或者在迁移到SQL Server 2008或更高版本时做好准备.
编辑
我认为你可能会追求的是一个INSTEAD OF
触发器(具有讽刺意味).这是一个例子.让我们考虑一个带有PK列和唯一列的非常简单的表:
CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO
Run Code Online (Sandbox Code Playgroud)
还有一个简单的日志表来捕获活动:
CREATE TABLE dbo.myLog
(
foobar_id INT,
oldValue XML,
newValue XML,
[action] CHAR(6),
success BIT
);
GO
Run Code Online (Sandbox Code Playgroud)
以下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
Run Code Online (Sandbox Code Playgroud)
让我们尝试一些非常简单的隐式事务语句:
-- 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
Run Code Online (Sandbox Code Playgroud)
检查日志:
SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;
Run Code Online (Sandbox Code Playgroud)
结果:
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
Run Code Online (Sandbox Code Playgroud)
当然,您可能需要日志表上的其他列,例如用户,日期/时间,甚至可能是原始语句.这并不是一个完全全面的审计解决方案,只是一个例子.
正如Mikael指出的那样,这依赖于外部批处理是启动隐式事务的单个命令这一事实.如果外部批处理是显式的多语句事务,则必须测试该行为.
另请注意,在UPDATE影响零行的情况下,这不会捕获"失败".因此,您需要明确定义"失败"的含义 - 在某些情况下,您可能需要在外部代码中构建自己的失败处理,而不是在触发器中.
归档时间: |
|
查看次数: |
4634 次 |
最近记录: |