SQL Server中的触发器 - 获取为Audit Table完成的事务类型

Anu*_*uya 0 sql-server triggers sql-server-2005

我正在写入将记录插入Audit表格的触发器.

只要目标表中的数据发生更改,触发器就会将旧值,新值更新到审计表

此外,还有名为Transaction和的列Transaction_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
Run Code Online (Sandbox Code Playgroud)

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