触发失败时MSSQL阻止回滚

时间:2011-03-31 10:59:24

标签: sql-server-2005 tsql triggers

我有一个插入/更新/删除触发器,每次对特定表进行插入/更新/删除时,它会在AuditTable中插入一条新记录。如果在AuditTable中插入失败,我希望无论如何都要插入第一条记录,并将错误记录在另一个表“AuditErrors”中。

这是我到目前为止所做的事情,我尝试了许多不同的东西,但是如果触发器插入AuditTable失败,我无法使用它(我通过在AuditTable插入中拼写错误拼写列的名称来测试)。注意:@sql是插入AuditTable。

DECLARE @TranCounter INT
SET @TranCounter = @@TRANCOUNT
IF @TranCounter > 0
  SAVE TRANSACTION AuditInsert;
ELSE
  BEGIN TRANSACTION;
BEGIN TRY
  EXEC (@sql)
  IF @TranCounter = 0
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
  -- roll back
  IF @TranCounter = 0
    ROLLBACK TRANSACTION;
  ELSE
    IF XACT_STATE() <> -1
      ROLLBACK TRANSACTION AuditInsert;
  -- insert error into database    
  IF @TranCounter > 0
    SAVE TRANSACTION AuditInsert;
  ELSE
    BEGIN TRANSACTION;
  BEGIN TRY
    INSERT INTO [dbo].[AuditErrors] ([AuditErrorCode], [AuditErrorMsg]) VALUES (ERROR_NUMBER(), ERROR_MESSAGE())
    IF @TranCounter = 0
      COMMIT TRANSACTION;
  END TRY
  BEGIN CATCH
    -- roll back
    IF @TranCounter = 0
      ROLLBACK TRANSACTION;
    ELSE
      IF XACT_STATE() <> -1
        ROLLBACK TRANSACTION AuditInsert;
  END CATCH
END CATCH

2 个答案:

答案 0 :(得分:2)

这是我知道将原始事务与触发器操作分开的唯一方法。在此示例中,即使审计插入失败,原始插入也会完成。在2008R2上进行了测试。

它不漂亮,但它不会回滚交易!

使用受信任的身份验证可以正常工作:

create table TestTable(
    ID int identity(1,1) not null
    ,Info varchar(50) not null
    )
GO
create table AuditTable(
    AuditID int identity(1,1) not null
    ,TestTableID int not null
    ,Info varchar(10) -- The failure is the mismatch in length
)
GO

create procedure insertAudit @id int, @Info varchar(50)
as
set nocount on;
begin try
    insert into AuditTable(TestTableID,Info)
    values(@id,@Info);
end try
begin catch
    select 0
end catch;
GO

create trigger trg_TestTable on TestTable
AFTER INSERT
as
begin
set nocount on;

declare @id int,
        @info varchar(50),
        @cmd varchar(500),
        @rc int;
select @id=ID,@info=Info from inserted;

select @cmd = 'osql -S '+@@SERVERNAME+' -E -d '+DB_NAME()+' -Q "exec insertAudit @id='+cast(@id as varchar(20))+',@Info='''+@info+'''"';

    begin try
        exec @rc=sys.xp_cmdshell @cmd
        select @rc;
    end try
    begin catch
        select 0;
    end catch;
end
GO

删除Audit表,它仍然完成原始事务。

干杯!

答案 1 :(得分:0)

您可以考虑使用BEGIN TRAN / ROLLBACK,而不是使用sqlcmd。

请注意,即使回滚命令将撤消自语句启动以来导致触发器触发的所有更改,但后续命令所做的任何更改都不会。

如果在审计表中插入数据的事务被回滚,那么您所要做的就是重复执行@sql中的代码:

TRIGGER BEGINS

<INSERT INSERTED AND DELETED TABLES INTO TABLE VARIABLES, U'LL NEED THEM>

BEGIN TRY

BEGIN TRAN

INSERT INTO AUDITTABLE SELECT * FROM @INSERTED

COMMIT

END TRY

BEGIN CATCH

ROLLBACK

REDO ORIGINAL INSERT/UPDATE/DELETE USING TRIGGER TABLE VARIABLES (@INSERTED AND @DELETED)

INSERT INTO AUDITERROS...

END CATCH

BEGIN TRAN -- THIS IS TO FOOL SQL INTO THINKING THERE'S STILL A TRANSACTION OPEN

TRIGGER ENDS