经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » MS SQL Server » 查看文章
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION
来源:cnblogs  作者:潇湘隐者  时间:2020/12/8 9:01:38  对本文有异议

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

 

Msg 3903, Level 16, State 1, Line 22

The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.

 

出现这种错误有很多种可能性,下面我们来通过一些案例来简单介绍一下这个错误,这些案例都是一些特殊案例的简化版本。

 

案例1:

 

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

 

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

 

 

clip_image001

 

 

如果要在捕获一个事务里面出现异常的正确的做法如下所示,个人更倾向于第二种写法。

 

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

 

 

案例2:

 

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

 

 

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

 

 

正确的SQL:

 

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

 

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

 

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

 

 

案例3:

 

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

 

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

 

总结:

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

原文链接:http://www.cnblogs.com/kerrycode/p/14035563.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号