TSQL:触发器中的Try-Catch事务

时间:2009-05-19 18:28:26

标签: sql-server tsql triggers

我正在尝试使用Microsoft Server 2005在触发器中放置try-catch语句。

BEGIN TRANSACTION
BEGIN TRY
    --Some More SQL
    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF (XACT_STATE()) = -1
    BEGIN
        ROLLBACK TRANSACTION;
    END;
END CATCH

问题是如果try-catch块捕获了某些内容,我不希望触发器失败。目前,我收到错误“交易在触发器中结束。批次已中止。”如果交易失败。如何让触发器优雅地失败?


此外,如果我删除了该事务,我会收到错误“事件在触发器中注定。批处理已中止。”。

BEGIN TRY
    --Some More SQL
END TRY
BEGIN CATCH
    return
END CATCH

有什么方法吗?

9 个答案:

答案 0 :(得分:8)

根据我的经验,触发器中try catch中捕获的任何错误都将回滚整个事务;您可以使用保存交易。我想你需要看看“更多sql”中发生的事情,并确定你是否可以编写case / if语句来阻止错误。

根据您正在做的事情,您可能会使用save transaction并在捕获中捕获

在你的代码中有类似的东西

SAVE TRANSACTION BeforeUpdate;
BEGIN TRY
        --Some More SQL
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION BeforeUpdate;
        return
END CATCH

答案 1 :(得分:7)

不要在触发器中回滚,也不需要启动事务。

ROLLBACK TRANSACTION也将回滚原始DML触发器额外的触发器事务。因此批次将被中止

编辑:

我建议你的catch块没有“RETURN”,只需让代码完成即可 我从来没有忽略过触发器中的陷阱错误(但我确实在使用rollback和raiserror的触发器中使用TRY / CATCH来重新抛出)所以这是猜测,但是返回可能是触发器中的异常退出条件

另外,首先尝试避免错误情况。更改--some more sql以避免错误。例如,添加if exists(...以测试重复的第一个或类似的

答案 2 :(得分:3)

使用SET XACT_ABORT OFF。当Transact-SQL语句遇到错误时,它只是引发了错误消息并且事务继续处理。 以下代码用于创建触发器:

Create TRIGGER [dbo].tr_Ins_Table_Master ON [dbo].Table_Master
 AFTER INSERT
AS
BEGIN
set xact_abort off
BEGIN TRY
        --your SQL          
        INSERT INTO Table_Detail
        SELECT MasterID,Name FROM INSERTED

END TRY

BEGIN CATCH     
    select ERROR_MESSAGE()
END CATCH

END

答案 3 :(得分:2)

为避免在触发操作之前丢失事务数据,您需要调用COMMIT TRAN。在TRY / CATCH块之前执行此操作,您将获得所需的结果。

示例:

COMMIT TRAN
BEGIN TRY
    -- possible error occurs here...
END TRY
BEGIN CATCH
    PRINT 'Error on line ' + CAST(ERROR_LINE() AS VARCHAR(10))
    PRINT ERROR_MESSAGE()
END CATCH

它仍会抛出以下错误 - 不确定如何避免:

The transaction ended in the trigger. The batch has been aborted.

但是原始事务和触发器事务都应该成功提交。

更新: 要避免异常上一个错误,请在TRY语句中调用BEGIN TRAN。注意,Microsoft建议不要在触发器中调用COMMIT TRAN,但如果不可避免,这应该适合您。

示例:

COMMIT TRAN
BEGIN TRY
    BEGIN TRAN

答案 4 :(得分:1)

u07ch,

不幸的是你不能使用保存事务并尝试..一起捕捉 - 它们根本无法一起工作:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/11/15/avoid-mixing-old-and-new-styles-of-error-handling.aspx

答案 5 :(得分:1)

了解您在触发器中尝试做什么可能会有所帮助。

触发器是将数据发送到插入或删除表的事务的一部分。如果失败,它将回滚整个事务。如果您希望触发器偶尔失败但不回滚使触发器触发的语句,那么您可能需要重新考虑触发器是否正确使用。

答案 6 :(得分:1)

这不是最好的方式,但它有效。启动一个新事务并执行正常的提交回滚,并在最后开始另一个事务以进行隐式事务提交

http://msdn.microsoft.com/en-us/library/ms187844(v=SQL.90).aspx

答案 7 :(得分:1)

这个演示实现了上面提到的许多事情。错误消息变为可选。使它工作的技巧是在嵌套的动态执行中。

    if object_id('toto')  is not  null drop table toto
    go
    create table toto (i int);
    go
    if object_id('toto2')  is not  null drop table toto2
    go
    create table toto2 (i int);
    go
    create Trigger trtoto
    ON toto
    Instead Of Insert
    as
    Begin
      BEGIN TRY
        set nocount  on
        insert into  toto  values(2)

        declare @sql nvarchar(max) =  'insert into toto2 values(3); select * from ThisTableDoesntexist'

        Exec sp_executeSql N'set xact_abort off; exec (@sql) ', N'@sql nvarchar(max)', @sql

      END TRY

      BEGIN CATCH
        PRINT  'Error on line ' + CAST(ERROR_LINE() AS VARCHAR(10))
        PRINT ERROR_MESSAGE()
      END CATCH
    End

GO
-- tests
set nocount on
insert into toto values (1)  -- is not inserted on purpose by  the trigger
select * from toto   -- other value inserted despite the error
select * from toto2  -- other value inserted in other table despite the error

答案 8 :(得分:0)

您可以在触发开始时将XACT_Abort设置为OFF。