-
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION
如果你的存储过程或其他脚本出现下面这个错误,一般是因为ROLLBACK TRANSACTION在逻辑上缺少匹配的BEGIN TRANSACTION或者没有开始一个事务(也有可能此事务已经提交),但是你做了事务回滚操作(ROLLBACK TRANSACTION),否则就可能出现这种错误。
Msg 3903, Level 16, State 1, Line 22
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
出现这种错误有很多种可能性,下面我们来通过一些案例来简单介绍一下这个错误,这些案例都是一些特殊案例的简化版本。
案例1:
如果你执行存储过程PRC_TEST,如下所示,因为执行存储过程dbo.PRC_EXC时遇到异常被捕获,此时在BEGIN CATCH部分执行ROLLBACK TRAN TT,但是实际上,此事务已经提交,数据库根本没有这样一个事务,然后你又要回滚事务,所以出错。可能让人好奇的是为什么存储过程dbo.PRC_EXC不放在事务里面,这里仅仅是简单模拟生产环境的一个案例,正确的做法应该将dbo.PRC_EXC放入事务当中,或者将dbo.PRC_EXC放入另外一个BEGIN TRY ... END TRY里面去。
如果要在捕获一个事务里面出现异常的正确的做法如下所示,个人更倾向于第二种写法。
案例2:
上面错误的原因,在于没有异常或错误时,事务提交后,这一句“ERROR_HANDLER: ROLLBACK TRANSACTION”总是会被执行,逻辑上已经没有事务了。所以正确的做法,事务提交后,直接RETURN,避免正常情况下执行ERROR_HANDLER: ROLLBACK TRANSACTION,或者将回滚逻辑放到IF条件之后,不要用GOTO这种写法.
正确的SQL:
这里来一个简单的演示,你可以体会一下,所谓的BEGIN TRAN与ROLLBACK TRANSACTION并不是指数量匹对,而是逻辑上事务回滚前,必须有一个未提交的事务。
案例3:
下面这种错误,纯属菜鸟级别犯的错误或粗心大意所致。
总结:
实际案例中,如果存储过程里面有复杂的业务逻辑,尤其出现嵌套调用存储过程的时候,特别是多层嵌套时,这种问题排查起来也相当麻烦。所以尽量少用嵌套调用存储过程。简化业务逻辑!另外,出现这种错误时,需要仔细检查代码逻辑才能找出这些出错的地方,似乎也没有其它更好的方法。
出处:https://www.cnblogs.com/kerrycode/p/14035563.html
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;
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里面去。
如果要在捕获一个事务里面出现异常的正确的做法如下所示,个人更倾向于第二种写法。
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:
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:
下面这种错误,纯属菜鸟级别犯的错误或粗心大意所致。
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;
总结:
实际案例中,如果存储过程里面有复杂的业务逻辑,尤其出现嵌套调用存储过程的时候,特别是多层嵌套时,这种问题排查起来也相当麻烦。所以尽量少用嵌套调用存储过程。简化业务逻辑!另外,出现这种错误时,需要仔细检查代码逻辑才能找出这些出错的地方,似乎也没有其它更好的方法。
出处:https://www.cnblogs.com/kerrycode/p/14035563.html
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式