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

Posted

技术标签:

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

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

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

Exec USPStoredProcName

我收到以下错误:

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

【问题讨论】:

您的程序中有任何 TRY/CATCH 块吗? 是的,我有 TRY / CATCH 块 【参考方案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的问题。

以上是关于EXECUTE 之后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配。先前计数 = 1,当前计数 = 0的主要内容,如果未能解决你的问题,请参考以下文章

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

SqlServer中嵌套事务使用--事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配 --根本问题

为啥我的存储过程在包含在事务块中时会抛出错误?

坑到了,EF执行带事物的存储过程

sqlit3事务

单数行灰色,双数行白色