TRY/CATCH块中执行EXEC存储过程是否会提交事务?
问题原因分析
出现该问题的核心是存储过程内部的事务操作修改了全局事务计数:
- 外层执行
BEGIN TRAN后,全局的@@TRANCOUNT值会变为1,INSERT Record操作被归入这个未提交的事务中 - 你调用的存储过程内部主动执行了
COMMIT TRAN或者ROLLBACK TRAN操作,会把@@TRANCOUNT直接减到0,外层开启的事务被提前结束:如果存储过程执行的是COMMIT,那么你外层的INSERT Record就会被同步提交;如果执行的是ROLLBACK,INSERT Record会被提前回滚 - 代码进入CATCH块执行ROLLBACK的时候,
@@TRANCOUNT已经是0,自然会抛出"The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION"的错误 - 替换为普通INSERT报错可以正常回滚,是因为普通SQL报错不会主动操作事务计数,外层事务还处于活跃状态,ROLLBACK可以正常执行
还有一种少见场景是存储过程触发了严重级别≥20的系统错误,会直接终止并自动回滚所有事务,也会导致@@TRANCOUNT清零。
修复方案
方案1:调整存储过程事务逻辑(优先选择)
修改存储过程的事务处理逻辑,让它仅在没有外层事务的时候自主管理事务,有外层事务时把控制权交还给外层:
-- 存储过程内部新增事务判断逻辑 DECLARE @IsOuterTranExist BIT = CASE WHEN @@TRANCOUNT > 0 THEN 1 ELSE 0 END IF @IsOuterTranExist = 0 BEGIN TRAN -- 存储过程原有业务逻辑 -- 原有逻辑里的ROLLBACK/COMMIT操作全部删除,报错直接用THROW抛出给外层处理 IF @IsOuterTranExist = 0 COMMIT TRAN
调整后存储过程不会再主动结束外层事务,外层的TRY/CATCH逻辑就可以正常工作。
方案2:外层增加事务状态判断
如果没有权限修改存储过程代码,可以在外层CATCH块执行回滚前先判断事务状态,避免无事务可回滚的报错:
SET XACT_ABORT ON -- 新增配置,只要出错事务就会进入不可提交状态,避免部分提交 BEGIN TRY BEGIN TRAN INSERT Record --Business validation EXEC StoredProcedure --This should throw error PRINT 'Commit Tran' COMMIT TRAN END TRY BEGIN CATCH PRINT 'In CATCH Block' -- 仅存在活跃事务时才执行回滚 IF @@TRANCOUNT > 0 OR XACT_STATE() <> 0 ROLLBACK TRAN; END CATCH PRINT 'After END CATCH'
内容的提问来源于stack exchange,提问作者bbeck
相关产品推荐
相关产品推荐

