单个事务的多次回滚

Posted

技术标签:

【中文标题】单个事务的多次回滚【英文标题】:Multiple Roll back for single Transcation 【发布时间】:2016-06-23 15:54:48 【问题描述】:

我必须更新表中的多个行。 我的要求是,如果由于某种原因,更新结果返回 0,那么应该回滚整个事务。 同时如果有异常发生,那么整个事务也必须回滚。

简而言之,如果更新语句返回 0 或更新表时发生任何异常,我需要回滚整个更新事务。

这是我使用的代码。

CREATE TYPE [DBO].[EMPLOYEETYPETABLETYPE] AS TABLE
( EmployeeStatusKey INT, EmployeeStatusName VARCHAR(50) )

CREATE PROCEDURE [dbo].[usp_UpdateEmployeeStatusType]

@EmploymentStatusDetails [DBO].[EMPLOYEETYPETABLETYPE] READONLY 

AS
 BEGIN

SET NOCOUNT ON;

DECLARE @TransactionName varchar(20) = 'UpdateEmployeeStatus';

DECLARE @rowcount1 INT

    BEGIN

    BEGIN TRY
        BEGIN TRANSACTION @TransactionName

            UPDATE  ES1
            SET
                ES1.EmployeeStatusName=ES2.EmployeeStatusName

            FROM 
                 [dbo].[EmployeeStatusTypes] ES1
            INNER JOIN 
                @EmploymentStatusDetails ES2
            ON 
                ES1.EmployeeStatusKey= ES2.EmployeeStatusKey

            SET
                @ROWCOUNT1=@@ROWCOUNT

            IF @rowcount1 =0
                GOTO PROBLEM

        PROBLEM:
        ROLLBACK TRAN  @TransactionName
    COMMIT
END TRY
    BEGIN CATCH
        SET @ROWCOUNT1=0
        ROLLBACK TRAN @TransactionName
    END CATCH

    IF @rowcount1 =0
        SELECT  -178,@rowcount1;
    ELSE
        SELECT  178,@rowcount1;
    END 


 END

我正在将数据表从 C# 代码传递给存储过程。 当我执行 Sp 时,没有抛出错误但是当我从 C# 代码中调用它时,我得到了异常

异常:ROLLBACK TRANSACTION 请求没有对应的 BEGIN TRANSACTION。

请提前帮助和感谢....

【问题讨论】:

为什么要使用命名事务? 我还建议你在多个地方打印@@trancount,看看你为什么会遇到这个问题。 @FLICKER 刚刚在教程中看到。 【参考方案1】:

我用简化的脚本测试了你的脚本:

BEGIN TRY
    print 'A:' + cast(@@trancount as varchar)
    BEGIN TRANSACTION tranName

    print 'B:' + cast(@@trancount as varchar)
    GOTO PROBLEM -- you can comment this to simulate there is no error

    PROBLEM:
    begin
      ROLLBACK TRAN  tranName
      print 'there is a problem'
    end
    print 'C:' + cast(@@trancount as varchar)
    COMMIT
END TRY
BEGIN CATCH
    print 'D:' + cast(@@trancount as varchar)
    ROLLBACK TRAN tranName
END CATCH

当有问题时,输出是这样的

A:0
B:1
there is a problem
C:0
D:0
Msg 3903, Level 16, State 1, Line 18
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.

如果我评论GOTO PROBLEM所以没有问题,输出是这样的:

A:0
B:1
there is a problem
C:0
D:0
Msg 3903, Level 16, State 1, Line 18
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.

您会看到 GOTO 下的部分仍在执行中。

最后,COMMIT 仍然发生在没有可用事务的地方,所以COMMIT 抛出并出错。这意味着您的脚本会引发异常。

如果你摆脱 GOTO,你会很好。无论如何,使用GOTO 是一种不好的做法。

【讨论】:

【参考方案2】:

删除那个糟糕的 GOTO

IF @rowcount1 =0
  ROLLBACK TRAN  @TransactionName
ELSE
  COMMIT TRAN @TransactionName

并查看 TRY-CATCH 文档。您必须通过检查 XACT_STATE() 值来检查是否有任何事务要提交或回滚。

【讨论】:

如果我去掉goto,那么如果update语句返回0怎么办(说主键=0时的条件) 好吧,我为您提供了一些以if 开头的代码。现在,您的代码将在 ROLLBACK 之后立即在无条件 COMMIT 上失败。

以上是关于单个事务的多次回滚的主要内容,如果未能解决你的问题,请参考以下文章

外部事务失败时回滚内部事务

SQL Server事务执行一半出错是否自动回滚整个事务

Oracle事务的提交与回滚

MySql 的事务隔离级别

MySQL之事务

MySQL的事务隔离级别