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

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

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

Exec USPStoredProcName

我得到以下错误:

Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.

我已经阅读了其他类似问题的答案,但是无法找到提交计数被搞乱的确切位置。

283090 次浏览

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

我建议您仔细阅读 异常处理和嵌套事务,它显示了一个可用于嵌套事务和异常的模式:

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

这通常发生在事务启动时,事务要么没有提交,要么没有回滚。

如果错误出现在存储过程中,这可能会锁定数据库表,因为在没有异常处理的情况下,由于某些运行时错误,事务没有完成 您可以像下面这样使用异常处理。 < a href = “ http://msdn.microsoft.com/en-us/library/ms188792.aspx”rel = “ norefrer”> 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

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

return
commit

而不是

commit
return

在一个存储过程中。

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

这可能与 TRY/CATCH 结合使用会导致您所描述的错误。

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

COMMIT TRANSACTION [MyTransactionName]

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

我发现我必须使用2个存储过程一个“工作者”一个和一个包装器一个与尝试/捕捉都与逻辑类似的 Remus Rusanu 概述。辅助捕获用于处理“正常”故障,包装捕获用于处理编译故障错误。

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

未受 TRY... CATCH 构造影响的错误

CATCH 块 when they occur at the same level of execution不作为 TRY... CATCH 构造处理下列类型的错误:

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

希望这能帮助其他人节省几个小时的调试时间。

对于我来说,经过大量的调试之后,这个修复只是一个简单的丢失; 回滚之后 catch 中的语句。如果没有它,这个丑陋的错误消息就是您最终得到的结果。

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

如果你的代码结构是这样的:

SELECT 151
RETURN -151

然后使用:

SELECT 151
ROLLBACK
RETURN -151

在我看来,公认的答案在大多数情况下是过分的。

错误的原因通常是 BEGIN 和 COMMIT 不匹配,正如错误清楚地说明的那样。这意味着使用:

Begin
Begin
-- your query here
End
commit

instead of

Begin Transaction
Begin
-- your query here
End
commit

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

我有相同的错误消息,我的错误是我有一个分号在提交交易行的结尾

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

在我的例子中,我不小心在查询中出现了 BEGINTRAN 语句

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

在我的例子中,错误是由 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

避免使用

RETURN

当你使用

BEGIN TRY
...
END TRY


BEGIN CATCH
...
END CATCH

and

BEGIN, COMMIT & ROLLBACK

SQL 存储过程中的语句

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

此消息的确切原因是 SQLServer 暗示的规则: 过程的执行开始和结束时的事务计数应该相同。换句话说,程序;

  • shouldn't commit/rollback a transaction that it didn't start. In this case, previous count displayed in the exception message would be greater zero, and current count is zero. Best way to prevent this is capturing transaction count (@@TRANCOUNT) at the very beginning of the execution, and using transaction statements only if it is zero. The sample procedure below is a simplest "safe" structure against this type of mistake. If this procedure is called within an existing transaction, it won't begin a new transaction nor try to commit or rollback the "inherited" one. Instead, it just re-throws the same error to caller context. This is also a good practice to keep the real source procedure of the error.
  • 应该在执行结束之前决定它启动的事务的命运(提交或回滚)。在这种情况下,当前计数将大于以前的计数。

我强烈建议大家仔细阅读 Erland Sommarskog 的 SQLServer 中的错误和事务处理


create or alter proc sp_err266
as
begin
set nocount on
set xact_abort on


declare @trancount int = @@trancount


if @trancount = 0
begin tran


begin try


raiserror('Raise an unexpected error...', 16, 1);
        

if XACT_STATE() = 1 and @trancount = 0
commit;


end try
begin catch
if XACT_STATE() <> 0 and @trancount = 0
rollback;
else
throw;
end catch
end

对我来说,两个开始事务和多回滚事务导致了这个问题。

------------------------------------------------------------
BEGIN TRANSACTION
-- BEGING TRANSACTION
call of stored procedure   -- ROLLBACK TRANASCTION
-- ROLLBACK TRANSACTION
ROLLBACK TRANSACTION
-----------------------------------------------------------

它只能回滚一次,不会有多个回滚语句,也会检查导致问题的 return 语句。

在嵌套过程中,应该谨慎使用 ROLLback,这里详细解释 https://stackoverflow.com/a/74479802/6204480