【问题标题】:TSQL - Error when running a procedure within a transaction where the procedure includes a transactionTSQL - 在包含事务的事务中运行过程时出错
【发布时间】:2019-10-25 15:48:47
【问题描述】:

在存储过程中,我有一个带有事务的 try catch 块,如果控制永远不会传递给 catch,它将提交,否则如果出现错误并且控制确实传递给 catch 块,则会回滚。

我正在事务中运行此存储过程,如下例所示:

 CREATE PROCEDURE sp_test
 AS
 BEGIN
     BEGIN TRY
         BEGIN TRAN
             DECLARE @var INT = 1
             IF @var <> 2 
                 BEGIN
                     RAISERROR('error', 16,1)
                 END
         COMMIT
     END TRY
     BEGIN CATCH
         ROLLBACK
         PRINT 'Rolled back'
     END CATCH
 END


 BEGIN TRAN

 EXEC sp_test

当我运行它时,我看到了错误

消息 266,级别 16,状态 2,过程 sp_test,第 0 行 [批处理开始第 7 行]
EXECUTE 之后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配。先前计数 = 1,当前计数 = 0。

但我不明白为什么。

我正在存储过程之外开始一个事务。然后我将进入 SP 并开始另一笔交易。一旦控制传递到 catch 块,两个事务都应该回滚,所以事务计数应该是 0。

显然这里我的理解有差距。

【问题讨论】:

标签: sql-server stored-procedures transactions try-catch


【解决方案1】:

此行为是设计使然。每当启动事务时,@@TRANCOUNT 会话变量将增加一,而当提交事务时,它会减少一。但是,当事务回滚时,它会回滚所有嵌套事务。因此,在您的情况下,您的 catch 中的回滚将回滚 2 个嵌套事务,@@TRANCOUNT 将变为 0。这将导致调用者抛出不匹配事务计数异常。

为避免此问题,您可以在 SP 中检查 @@TRANCOUNT 并仅在它为 0 时启动新事务并设置一个标志(局部变量)来指示,例如@new_tarn=1。然后你只在@new_tran=1 时提交或回滚。

【讨论】:

  • 同样在 catch 中,您需要重新抛出异常,以便调用者有机会捕获它并在调用者启动事务时回滚事务。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-02
  • 1970-01-01
  • 1970-01-01
  • 2015-06-24
  • 1970-01-01
  • 1970-01-01
  • 2015-05-20
相关资源
最近更新 更多