The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION

若是你的存储过程或其余脚本出现下面这个错误,通常是由于ROLLBACK TRANSACTION在逻辑上缺乏匹配的BEGIN TRANSACTION或者没有开始一个事务(也有可能此事务已经提交),可是你作了事务回滚操做(ROLLBACK TRANSACTION),不然就可能出现这种错误。数据库

 

Msg 3903, Level 16, State 1, Line 22app

The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.spa

 

出现这种错误有不少种可能性,下面咱们来经过一些案例来简单介绍一下这个错误,这些案例都是一些特殊案例的简化版本。code

 

案例1:orm

 

CREATE PROCEDURE PRC_EXC
AS
BEGIN
    SELECT 1/0  --仅仅模拟存储过程出现异常。
END;
GO 
 
CREATE PROCEDURE PRC_TEST
AS
BEGIN
 
    BEGIN TRY
        BEGIN TRAN TT
 
            UPDATE dbo.TEST SET NAME='k3' WHERE object_id =9;
 
        COMMIT TRAN TT;
 
        EXEC dbo.PRC_EXC
 
    END TRY
    BEGIN CATCH
        ROLLBACK TRAN TT;
    END CATCH
END

 

若是你执行存储过程PRC_TEST,以下所示,由于执行存储过程dbo.PRC_EXC时遇到异常被捕获,此时在BEGIN CATCH部分执行ROLLBACK TRAN TT,可是实际上,此事务已经提交,数据库根本没有这样一个事务,而后你又要回滚事务,因此出错。可能让人好奇的是为何存储过程dbo.PRC_EXC不放在事务里面,这里仅仅是简单模拟生产环境的一个案例,正确的作法应该将dbo.PRC_EXC放入事务当中,或者将dbo.PRC_EXC放入另一个BEGIN TRY ... END TRY里面去。blog

 

 

clip_image001

 

 

若是要在捕获一个事务里面出现异常的正确的作法以下所示,我的更倾向于第二种写法。事务

 

BEGIN TRANSACTION;
 
BEGIN TRY
 
   ...................
   ...................
   --执行全部业务逻辑后,最后提交
   COMMIT;
 
END TRY
 
BEGIN CATCH
 
   --if an exception occurs execute your rollback, also test that you have had some successful transactions
   IF @@TRANCOUNT > 0 ROLLBACK
 
 
 
END CATCH
 
 
 
 
BEGIN TRY
 
   BEGIN TRANSACTION;
   ....................
   ....................
   --执行全部业务逻辑后,最后提交
   COMMIT;
 
END TRY
 
BEGIN CATCH
 
   --if an exception occurs execute your rollback, also test that you have had some successful transactions
   IF @@TRANCOUNT > 0 ROLLBACK
 
END CATCH

 

 

案例2:ip

 

CREATE PROCEDURE PRC_TEST2
AS 
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRANSACTION
 
    UPDATE dbo.TEST SET NAME='k3' WHERE object_id =9;    --这里用简单的UPDATE替换复杂的业务逻辑。
 
    IF @@Error != 0 GOTO ERROR_HANDLER
 
     UPDATE dbo.TEST SET NAME='k3' WHERE object_id =15; --这里用简单的UPDATE替换复杂的业务逻辑。
    
    IF @@Error != 0 GOTO ERROR_HANDLER
 
COMMIT TRANSACTION
    
ERROR_HANDLER: ROLLBACK TRANSACTION
SET NOCOUNT OFF
RETURN 0
GO

 

 

上面错误的缘由,在于没有异常或错误时,事务提交后,这一句ERROR_HANDLER: ROLLBACK TRANSACTION老是会被执行,逻辑上已经没有事务了。因此正确的作法,事务提交后,直接RETURN,避免正常状况下执行ERROR_HANDLER: ROLLBACK TRANSACTION,或者将回滚逻辑放到IF条件以后,不要用GOTO这种写法.ci

 

 

正确的SQL:get

 

ALTER PROCEDURE PRC_TEST2
AS 
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRANSACTION
 
    UPDATE dbo.TEST SET NAME='k3' WHERE object_id =9;    --这里用简单的UPDATE替换复杂的业务逻辑。
 
    IF @@Error != 0 GOTO ERROR_HANDLER
 
     UPDATE dbo.TEST SET NAME='k3' WHERE object_id =15;   --这里用简单的UPDATE替换复杂的业务逻辑。
IF @@Error != 0 GOTO ERROR_HANDLER
 
COMMIT TRANSACTION
SET NOCOUNT OFF
RETURN 0;
    
ERROR_HANDLER: ROLLBACK TRANSACTION
SET NOCOUNT OFF
RETURN 0
GO

 

这里来一个简单的演示,你能够体会一下,所谓的BEGIN TRAN与ROLLBACK  TRANSACTION并非指数量匹对,而是逻辑上事务回滚前,必须有一个未提交的事务。

 

SELECT * INTO test FROM sys.objects
 
SELECT  @@TRANCOUNT;--值为0
BEGIN TRAN
UPDATE  TEST SET     name = 'kkk' WHERE   object_id =7;
SELECT  @@TRANCOUNT;--值为1,
COMMIT TRAN
 
ROLLBACK TRAN;  --事务其实已经结束,忽然来一个回滚事务,没有匹配的BEGIN TRAN,因此出现报错"The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION."
 

 

 

案例3:

 

下面这种错误,纯属菜鸟级别犯的错误或粗枝大叶所致。

 

CREATE PROCEDURE PRC_TEST4
AS 
SET NOCOUNT ON
 
BEGIN
 
BEGIN TRY
 
    UPDATE dbo.TEST SET NAME='k3' WHERE object_id =9;    --这里用简单的UPDATE替换复杂的业务逻辑。
 
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION
END CATCH
END;

 

总结:

实际案例中,若是存储过程里面有复杂的业务逻辑,尤为出现嵌套调用存储过程的时候,特别是多层嵌套时,这种问题排查起来也至关麻烦。因此尽可能少用嵌套调用存储过程。简化业务逻辑!另外,出现这种错误时,须要仔细检查代码逻辑才能找出这些出错的地方,彷佛也没有其它更好的方法。

相关文章
相关标签/搜索