【问题标题】:Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0EXECUTE 之后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配。先前计数 = 1,当前计数 = 0
【发布时间】:2014-03-22 17:10:03
【问题描述】:

我有一个Insert 存储过程,它将向Table1 提供数据,并从Table1 获取Column1 值,然后调用第二个存储过程来提供给Table2。

但是当我调用第二个存储过程为:

Exec USPStoredProcName

我收到以下错误:

EXECUTE 之后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配。先前计数 = 1,当前计数 = 0。

我已阅读其他此类问题的答案,但无法找到提交计数究竟在哪里弄乱了。

【问题讨论】:

  • 您的程序中有任何 TRY/CATCH 块吗?
  • 是的,我有 TRY / CATCH 块

标签: sql sql-server-2012 sqlexception


【解决方案1】:

对我来说,问题是我忘记在事务中 SP 调用的一些输出参数之后添加 output 关键字。

【讨论】:

    【解决方案2】:

    避免使用

    RETURN
    

    使用时的声明

    BEGIN TRY
        ... 
    END TRY
    
    BEGIN CATCH
        ...
    END CATCH
    

    BEGIN, COMMIT & ROLLBACK
    

    SQL 存储过程中的语句

    【讨论】:

      【解决方案3】:

      在我的例子中,错误是由BEGIN TRANSACTION 中的RETURN 引起的。所以我有这样的事情:

      Begin Transaction
       If (@something = 'foo')
       Begin
           --- do some stuff
           Return
       End
      commit
      

      它必须是:

      Begin Transaction
       If (@something = 'foo')
       Begin
           --- do some stuff
           Rollback Transaction ----- THIS WAS MISSING
           Return
       End
      commit
      

      【讨论】:

        【解决方案4】:

        这还取决于您从 C# 代码调用 SP 的方式。如果 SP 返回某个表类型值,则使用 E​​xecuteStoreQuery 调用 SP,如果 SP 没有返回任何值,则使用 E​​xecuteStoreCommand 调用 SP

        【讨论】:

          【解决方案5】:

          确保在同一过程/查询中没有多个事务,其中一个或多个未提交。

          在我的例子中,我不小心在查询中有一个 BEGIN TRAN 语句

          【讨论】:

            【解决方案6】:

            我有同样的错误信息,我的错误是我在 COMMIT TRANSACTION 行的末尾有一个分号

            【讨论】:

            • 就这么简单。另外,我的案例需要一个“ROLLBACK”语句,以防 SP 无法完全执行。仅用于关闭/结束交易。
            【解决方案7】:

            在我看来,接受的答案在大多数情况下是矫枉过正的。

            错误的原因通常是错误中明确说明的 BEGIN 和 COMMIT 不匹配。这意味着使用:

            Begin
              Begin
                -- your query here
              End
            commit
            

            而不是

            Begin Transaction
              Begin
                -- your query here
              End
            commit
            

            在 Begin 之后省略 Transaction 会导致此错误!

            【讨论】:

              【解决方案8】:

              如果您的代码结构类似于:

              SELECT 151
              RETURN -151
              

              然后使用:

              SELECT 151
              ROLLBACK
              RETURN -151
              

              【讨论】:

                【解决方案9】:

                对我来说,经过大量调试后,修复是一个简单的丢包;回滚后的 catch 语句。没有它,你最终会得到这个丑陋的错误消息。

                begin catch
                    if @@trancount > 0 rollback transaction;
                    throw; --allows capture of useful info when an exception happens within the transaction
                end catch
                

                【讨论】:

                  【解决方案10】:

                  如果您的存储过程在打开事务后遇到编译失败(例如未找到表、列名无效),也会发生这种情况。

                  我发现我必须使用 2 个存储过程,一个是“worker”,一个是包装器,两者都具有类似于 Remus Rusanu 概述的逻辑。 worker catch 用于处理“正常”失败,wrapper catch 用于处理编译失败错误。

                  https://msdn.microsoft.com/en-us/library/ms175976.aspx

                  不受 TRY…CATCH 构造影响的错误

                  以下类型的错误不会由 CATCH 块处理当它们发生在与 TRY...CATCH 构造相同的执行级别时

                  • 编译错误,例如语法错误,导致批处理无法运行。
                  • 语句级重新编译期间发生的错误,例如由于延迟名称解析而在编译后发生的对象名称解析错误。

                  希望这可以帮助其他人节省几个小时的调试时间...

                  【讨论】:

                  • 谢谢贾斯汀。很好的观察。在我的情况下,我在更新中进行聚合,它在 SP 保存期间不会产生编译错误,但确实是无效的语法 - “聚合可能不会出现在 UPDATE 语句的集合列表中”
                  【解决方案11】:

                  在我的事务中省略此语句后,我遇到过一次此错误。

                  COMMIT TRANSACTION [MyTransactionName]
                  

                  【讨论】:

                    【解决方案12】:

                    请注意,如果您使用嵌套事务,ROLLBACK 操作会回滚所有嵌套事务,包括最外层的事务。

                    这可能与 TRY/CATCH 结合使用,导致您描述的错误。查看更多here

                    【讨论】:

                      【解决方案13】:

                      我也有这个问题。对我来说,原因是我在做

                      return
                      commit
                      

                      而不是

                      commit
                      return   
                      

                      在一个存储过程中。

                      【讨论】:

                      • @seguso - 这很有帮助。谢谢你的分享。有时,一些如此简单的东西就隐藏在尘埃之下。发生在他们中最好的人身上。
                      • 这对我来说是个问题,但不太明显,因为我们通过数据访问层将多个存储过程调用包装在一个大事务中——因此,仅查看存储过程就无法判断存在一笔交易。如果您遇到此问题,请确保存储过程本身之外没有任何东西正在创建事务。如果有,那么您可能根本无法在存储过程中使用 return 语句。
                      • 这是我,我有一个事务并且在我的提交事务之前在 if/else 语句中返回
                      【解决方案14】:

                      这通常发生在事务启动且未提交或未回滚时。

                      如果错误出现在您的存储过程中,这可以锁定数据库表,因为在没有异常处理的情况下由于一些运行时错误导致事务未完成 您可以使用如下所示的异常处理。 SET XACT_ABORT

                      SET XACT_ABORT ON
                      SET NoCount ON
                      Begin Try 
                           BEGIN TRANSACTION 
                              //Insert ,update queries    
                           COMMIT
                      End Try 
                      Begin Catch 
                           ROLLBACK
                      End Catch
                      

                      Source

                      【讨论】:

                      • 如果是这种情况,引用的问题/答案可能意味着这个问题应该被标记为重复并关闭
                      【解决方案15】:

                      如果您有一个 TRY/CATCH 块,那么可能的原因是您正在捕获事务中止异常并继续。在 CATCH 块中,您必须始终检查 XACT_STATE() 并处理适当的中止和不可提交(注定)的事务。如果你的调用者启动了一个事务并且被调用者遇到了死锁(它中止了事务),那么被调用者将如何与调用者沟通事务被中止并且它不应该继续“照常营业”?唯一可行的方法是重新引发异常,强制调用者处理这种情况。如果你默默吞下一个中止的事务,而调用者继续假设仍在原始事务中,那么只有混乱可以确保(你得到的错误是引擎试图保护自己的方式)。

                      我建议您查看Exception handling and nested transactions,它显示了一种可用于嵌套事务和异常的模式:

                      create procedure [usp_my_procedure_name]
                      as
                      begin
                          set nocount on;
                          declare @trancount int;
                          set @trancount = @@trancount;
                          begin try
                              if @trancount = 0
                                  begin transaction
                              else
                                  save transaction usp_my_procedure_name;
                      
                              -- Do the actual work here
                      
                      lbexit:
                              if @trancount = 0
                                  commit;
                          end try
                          begin catch
                              declare @error int, @message varchar(4000), @xstate int;
                              select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
                              if @xstate = -1
                                  rollback;
                              if @xstate = 1 and @trancount = 0
                                  rollback
                              if @xstate = 1 and @trancount > 0
                                  rollback transaction usp_my_procedure_name;
                      
                              raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
                          end catch
                      end
                      go
                      

                      【讨论】:

                      • 感谢您的帮助。通过使用 Raiserror 我发现了问题。这是关于尝试将 NULL 值插入 NOT NULL 字段
                      • 但是约束检查验证不会中止事务。你是明确回滚,还是使用xact_abort on
                      • 我明确回滚
                      • 我已经尝试过这种模式,但它仍然不起作用 - 当我有一个外部事务时,这个模式会创建一个保存点,并且在发生严重错误(不可提交的事务)的情况下回滚外部事务 - 这仍然会导致进入程序前@@trancount = 1,退出程序时@@trancount = 0
                      • 我认为 CATCH 中的这一点是错误的:if @xstate = -1 rollback; 看看这个MSDN example,我们应该回滚整个事务,除非有不 一个外部事务(也就是说,除非我们做了begin tran)。我认为如果我们开始交易,该程序应该只有rollback,这将解决@sparrow的问题。
                      猜你喜欢
                      • 2014-10-25
                      • 1970-01-01
                      • 2016-07-21
                      • 2012-02-18
                      • 2015-06-23
                      • 2016-06-21
                      • 1970-01-01
                      • 2018-03-11
                      • 2015-12-21
                      相关资源
                      最近更新 更多