Sql server TSQL:尝试在触发器中捕获事务

Sql server TSQL:尝试在触发器中捕获事务,sql-server,tsql,triggers,Sql Server,Tsql,Triggers,我正在尝试使用MicrosoftServer2005在触发器中放入一个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块捕获了某些内容,我

我正在尝试使用MicrosoftServer2005在触发器中放入一个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块捕获了某些内容,我不希望触发器失败。目前,如果事务失败,我将收到错误“事务在触发器中结束。批处理已中止”。如何使触发器正常地失败


此外,如果删除该事务,则会出现错误“触发器中的事务注定失败。批处理已中止”


有没有办法解决这个问题?

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

回滚事务
将回滚原始DML触发器和额外触发器事务。因此,批处理将被中止

编辑:

我建议不要在catch块中使用“RETURN”,只允许代码完成 我从来没有忽略触发器中的捕获错误(但我确实使用TRY/CATCH-in触发器以及rollback和raiserror来重新抛出),因此这只是一个猜测,但返回可能是触发器中的异常退出条件


此外,首先要尽量避免出现错误情况。更改
——更多的sql
,以避免错误。例如,添加
if exists(…
以测试重复的first或类似的根据我的经验,在触发器中的try-catch中捕获的任何错误都将回滚整个事务;您可能可以使用save事务。我想您需要看看“更多sql”中发生了什么并确定是否可以围绕它编写case/if语句来停止错误

根据你所做的事情,你可以做的是使用一个工具,并在捕获中捕获它

在您的代码中,类似这样的内容

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

不幸的是,您无法使用save transaction并尝试一起捕获-它们无法一起工作:


了解您试图在触发器中执行的操作可能会有所帮助


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

为了避免在触发操作之前丢失事务数据,您需要调用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
COMMIT TRAN
BEGIN TRY
    BEGIN TRAN
它仍将抛出以下错误-不确定如何避免:

The transaction ended in the trigger. The batch has been aborted.
但是原始事务和触发器事务都应该成功提交

更新: 为避免上次出现异常错误,请在TRY语句中调用BEGIN TRAN。注意,Microsoft建议不要在触发器中调用COMMIT TRAN,但如果不可避免,这应该对您有效

例如:

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
COMMIT TRAN
BEGIN TRY
    BEGIN TRAN

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


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

此演示实现了上述许多功能。错误消息成为可选消息。使其工作的技巧是在嵌套的动态执行中

    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

使用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

这真的很有帮助。但是,我刚刚遇到了另一个问题。有关详细信息,请参阅更新的问题。不幸的是,触发器中没有
回滚
——这非常混乱,可能会完全破坏.NET事务支持!——我不知道有什么方法可以绝对强制触发器约束。
RAISERROR(…,16,1)
在许多情况下足以引起.NET的注意并引发异常,但“自动提交”事务(IMPLICIT_transactions=OFF)不受
RAISERROR
的影响,因此将绕过此触发器试图添加的任何约束。我找到的唯一“解决方案”是不使用“自动提交”事务,并使用TRY/CATCH.best.Design.Ever显式回滚/提交。(当然Sybase有一个完全可行的
回滚触发器
:-/)我不知道SAVE TRANSACTION+1有一段时间了:)它确实会影响性能,尽管使用非常少。这是否意味着我们必须有一个打开的事务?我们能在触发器的开头打开它吗?我也想知道@meir问题的答案。我在触发器中看到了这个“transaction is Destine”消息,据我所知,这是一个甚至没有包含事务的操作。@meir触发器总是有一个事务。当触发器启动时,如果一个数据库不存在,那么将为它创建一个。每当有任何东西插入到表中时,我都会尝试更新一个遗留数据库。问题是,如果触发器失败,我不希望插入失败。遗留数据库不是最可靠的系统。我将在更多的错误消息中使用“注定”一词。这是可以做到的。这只是一个皮塔。您需要知道是否创建了事务(以及如何创建),然后需要查看清理时的XACT_状态。。。这甚至不是在一个完全不同的丑陋的触发器的上下文中。我讨厌SQL Server事务和“异常处理”。@pst有时这是做不到的:如果您的事务注定失败,您就无法回滚到您的保存点。你所能做的就是回滚整个事务。当然。。。这个例子也涵盖了这种情况。那么用什么来代替呢?快到2021年了……编写了一个存储过程来对谷歌地图执行http请求……想在触发器后面使用它来进行地理编码……这是唯一一种不打扰封装触发器的事务的方法……做得好,Pelchat先生……现在我不必费心思考了关于异步tri