使用SAVE TRANSACTION时BEGIN与COMMIT语句数量不匹配问题求助
SQL事务嵌套与保存点回滚导致的事务计数不匹配问题
问题重现
先看最初的执行代码:
BEGIN TRANSACTION print @@TRANCOUNT EXECUTE proc1 print @@TRANCOUNT COMMIT TRANSACTION CREATE PROCEDURE proc1 AS BEGIN SET XACT_ABORT ON SAVE TRANSACTION TillOuterMost; BEGIN TRANSACTION print @@TRANCOUNT ROLLBACK TRANSACTION TillOuterMost; END GO
运行后抛出错误:
1 2 Msg 266, Level 16, State 2, Procedure proc1, Line 0 [Batch Start Line 12] Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 2. 2
原因很明确:回滚到保存点不会改变事务嵌套计数(@@TRANCOUNT)——它只撤销保存点之后的操作,但之前用BEGIN TRANSACTION提升的嵌套层级还在。
后来尝试直接去掉保存点用ROLLBACK TRANSACTION,结果又把内外层事务全回滚了:
1 2 Msg 266, Level 16, State 2, Procedure proc1, Line 0 [Batch Start Line 11] Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0. 0 Msg 3902, Level 16, State 1, Line 18 The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
这里不带保存点的ROLLBACK直接把*@@TRANCOUNT*清0了,外层的COMMIT自然找不到对应的事务。
换了另一种写法也没解决,还是报计数不匹配的错误。
解决方案
核心逻辑:嵌套事务里,BEGIN TRANSACTION会让*@@TRANCOUNT*加1,但回滚到保存点不会减;想要让计数回到调用前的状态,回滚保存点后需要执行一次COMMIT TRANSACTION——注意,嵌套事务的COMMIT并不会真的提交事务,只是把计数减1,直到外层的COMMIT才会真正完成提交。
修改后的存储过程:
CREATE PROCEDURE proc1 AS BEGIN SET XACT_ABORT ON SAVE TRANSACTION TillOuterMost; BEGIN TRANSACTION print @@TRANCOUNT -- 回滚到保存点,撤销当前嵌套事务内的所有操作 ROLLBACK TRANSACTION TillOuterMost; -- 提交一次,抵消之前的BEGIN TRANSACTION,把计数减回原来的数值 COMMIT TRANSACTION; END GO
再执行外层事务代码:
BEGIN TRANSACTION print @@TRANCOUNT EXECUTE proc1 print @@TRANCOUNT COMMIT TRANSACTION
这次运行输出会是:
1 2 1
完全符合预期,不会再报错。
为什么这么做?
- 外层事务启动后,@@TRANCOUNT是1
- 进入存储过程,
BEGIN TRANSACTION让计数变成2 - 回滚到保存点,操作被撤销,但计数还是2
- 执行
COMMIT TRANSACTION,计数减1回到1,和调用存储前的状态一致 - 最后外层的
COMMIT把计数清0,完成整个事务提交
这样既保留了外层事务的状态,又完美解决了事务计数不匹配的问题。
内容的提问来源于stack exchange,提问作者David Hovsepyan
相关产品推荐
相关产品推荐

