the rollback transaction request has no corresponding begin tran

在SQL Server中,如果你的存储过程或其他脚本出现下面这个错误,一般是因为ROLLBACK TRANSACTION在逻辑上缺少匹配的BEGIN TRANSACTION或者没有开始一个事务(也有可能此事务已经提交),但是你却做了事务回滚操作(ROLLBACK TRANSACTION),所以就出现了这种错误

Msg 3903, Level 16, State 1, Line 22 The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.

出现这种错误有很多种可能性,下面我们来通过一些案例来简单分析一下这个错误,这些案例都是工作中遇到的一些特殊案例的简化版本。仅供参考

案例1:


CREATE PROCEDURE PRC_EXC
AS
BEGIN
 SELECT 1/0  --仅仅模拟存储过程出现异常。
END;


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里面去。

如果要在捕获一个事务里面出现异常的正确的做法如下所示:

方法1:

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


方法2:

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:


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这种写法.

正确的SQL:

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:

下面这种错误,纯属菜鸟级别犯的错误或粗心大意所致。没有搞清楚显性事务(BEGIN TRANS/COMMIT TRANSACTION) 和BEGIN TRY的区别。混淆为一体了。

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;

总结:

实际案例中,如果存储过程里面有复杂的业务逻辑,尤其出现嵌套调用存储过程的时候,特别是多层嵌套时,这种问题排查起来也相当麻烦。所以尽量少用嵌套调用存储过程。简化业务逻辑!另外,出现这种错误时,需要仔细检查代码逻辑才能找出这些出错的地方,似乎也没有其它更好的方法

原文始发于微信公众号(DBA闲思杂想录):the rollback transaction request has no corresponding begin tran

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/227902.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!