2012-06-01 23 views
0

我正在写入触发器以将记录插入到我的Audit表中。在SQL Server中触发 - 获取为审计表完成的事务类型

每当有在我的目标表中的数据发生变化时,触发将更新旧值,新值审计表

此外,还有所谓的TransactionTransaction_Status

Transaction列列定义交易类型。可以是INSERT,UPDATEDELETE
Transaction_Status列表示SUCCESSFAILURE

如何实现这一目标?

我的触发器:

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 
+1

您的触发器是FOR UPDATE触发器。你将如何捕获INSERT和DELETE操作? –

+0

#Aaron Bertrand。那我该如何实现呢? – Anuya

+0

我的观点是你的触发器说'FOR UPDATE' ...需要'更新,插入,删除',如果你想捕获所有三个动作。 –

回答

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影响零行的情况下,这不会捕获“失败”。因此,您需要明确定义“失败”的含义 - 在某些情况下,您可能需要在外部代码中构建自己的失败处理,而不是触发器。

+0

#Aaron Bertrand,对不起,我帮你。你能解释一下吗? – Anuya

+0

+1,谢谢。需要一些测试用于多语句事务,并可能在回滚或提交外部事务之前对@@ trancount进行一些测试。 –

+0

@Mikael是的,没有保证暗示或打算,只是一个非常简单的POC。 –