EXECUTE之后的事务计数指示BEGIN和COMMIT语句的数量不匹配。上一个计数= 1,当前计数= 0


94

我有一个Insert存储过程,该存储过程将向其馈送数据Table1并从中获取Column1值,Table1并调用第二个存储过程,该第二个存储过程将馈入Table2。

但是当我将第二个存储过程称为:

Exec USPStoredProcName

我收到以下错误:

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

我已经阅读了其他此类问题的答案,无法找到确切的提交计数被弄乱的地方。


您的程序中是否有TRY / CATCH块?
Remus Rusanu 2014年

是的,我有TRY / CATCH区块
Vignesh Kumar A

Answers:


109

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

我建议您仔细研究异常处理和嵌套事务,该示例显示了可与嵌套事务和异常一起使用的模式:

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

3
谢谢你的帮助。通过使用Raiserror,我发现了问题。这是关于尝试将NULL值插入NOT NULL字段的问题
Vignesh Kumar A

但是约束检查验证不会中止事务。您是明确回滚还是使用xact_abort on
Remus Rusanu 2014年

我明确回滚
Vignesh Kumar A

2
我已经尝试过这种模式,但是仍然无法正常工作-当我有外部事务时,该模式会创建一个保存点,并且在发生严重错误(无法提交的事务)时回滚外部事务-这仍然会导致@@ trancount = 1才进入程序,退出时@@ trancount = 0
麻雀2015年

3
我觉得这一点在CATCH是错误的:if @xstate = -1 rollback; 看着这个MSDN例子,我们应该不会回滚整笔交易,除非有没有外部事务(也就是说,除非我们做了begin tran)。我认为该程序仅应rollback在我们开始交易时才能解决,这将解决@sparrow的问题。
尼克,

59

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

return
commit

代替

commit
return   

在一个存储过程中。


4
@seguso-这非常有帮助。感谢你的分享。有时,某些东西会因此而落入尘土。发生最好的事情。
Leo Gurdian

这对我来说是个问题,但不那么明显,因为我们是通过数据访问层将多个sproc调用包装在一个大事务中的-因此,仅查看该sproc,您根本无法知道有一个事务。如果遇到此问题,请确保存储过程本身之外没有任何东西正在创建事务。如果存在,那么您可能根本无法在sproc中使用return语句。
EF0

这是我,我进行了一笔交易,并在if / else语句中提交事务之前返回
Kevin

18

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

万一存储过程中出现错误,这可以锁定数据库表,因为在缺少异常处理的情况下由于某些运行时错误而导致事务未完成,因此可以使用以下异常处理。 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

资源


如果是这样的情况下,引用的问题/答案或许应该是说这一个应该被标记为重复的和封闭的
马克Schultheiss

10

请注意,如果您使用嵌套事务,那么ROLLBACK操作将回滚所有嵌套事务,包括最外面的一个。

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


5

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

我发现我必须使用2个存储过程,一个是“工作者”一个,另一个是使用try / catch的包装器,两者的逻辑都类似于Remus Rusanu概述的逻辑。辅助捕获用于处理“常规”故障,而包装捕获用于处理编译故障错误。

https://msdn.microsoft.com/zh-CN/library/ms175976.aspx

受TRY…CATCH构造影响的错误

当以下几种类型的错误与TRY…CATCH构造在相同的执行级别上发生时, CATCH块将不对其进行处理:

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

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


1
谢谢贾斯汀。很好的观察。就我而言,我正在做一个更新内的聚合,该更新在SP保存期间不会产生编译错误,但实际上是无效的语法-“聚合可能不会出现在UPDATE语句的设置列表中”
kuklei,

4

就我而言,该错误是由中的引起RETURNBEGIN TRANSACTION。所以我有这样的事情:

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

2

对我来说,经过大量调试后,修复很简单。回滚后在catch中声明。没有它,这个丑陋的错误消息就是最终的结果。

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

2

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


就这么简单。另外,在SP无法完全执行的情况下,我的案例需要使用“ ROLLBACK”语句。仅用于结束/结束交易。
J Cordero

1

从交易中删除此语句后,我一次遇到此错误。

COMMIT TRANSACTION [MyTransactionName]

1

我认为,在大多数情况下,可接受的答案是过大的。

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

Begin
  Begin
    -- your query here
  End
commit

代替

Begin Transaction
  Begin
    -- your query here
  End
commit

在开始之后省略事务会导致此错误!


1

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

就我而言,我不小心在查询中使用了BEGIN TRAN语句


1

这也可能取决于您从C#代码调用SP的方式。如果SP返回某个表类型值,则使用ExecuteStoreQuery调用SP,如果SP不返回任何值,则使用ExecuteStoreCommand调用SP。



0

如果您的代码结构如下:

SELECT 151
RETURN -151

然后使用:

SELECT 151
ROLLBACK
RETURN -151
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.