【问题标题】:How can @@trancount be zero within catch of a transaction?@@trancount 如何在事务捕获中为零?
【发布时间】:2020-05-17 09:40:05
【问题描述】:

我有这个带有事务和 try/catch 的语句块:

begin try
    begin transaction;
        insert ....
        update ....
        delete ....
    commit transaction;
end try
begin catch
    if (@@trancount > 0)
        rollback transaction;
    throw;
end catch;

我在这里使用@@trancount,但没有完全理解会发生什么。 为什么在这种情况下@@trancount 会为零?

【问题讨论】:

  • 一个例子是由INSERT调用的触发器,它回滚事务并抛出catch捕获的错误

标签: sql-server transactions try-catch


【解决方案1】:

接收@@trancount = 0 的最常见场景需要比您的示例使用的更详细的逻辑。

如果您在 try 块中调用了其他存储过程,他们可能对如何管理事务有自己的理解,并且由于代码编写不当或其他一些事故,他们可以提交外部意外事务,或回滚(记住,there is no really such thing in SQL Server as a nested transaction,因此任何不引用先前声明的保存点的rollback 语句都会清除所有内容)。这种情况下的错误可能会有所不同,要么首先导致内部过程行为不端,要么如果没有其他问题,您将得到error 266,“EXECUTE 后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配. 先前计数 = %ld,当前计数 = %ld。"

请注意,此行为也可能是由触发器执行的回滚引起的。

当您最终在catch 块中没有当前事务时,可能还有其他一些情况,但显然它们非常罕见,以至于我无法想到其他任何事情。

就个人而言,我尽可能对所有存储过程使用以下模板:

create procedure dbo.ProcTemplate
(
    @Error int = null output,
    @Message nvarchar(2048) = null output
) as

/*



20191223, RW - to be completed
*/

set nocount, quoted_identifier, ansi_nulls, ansi_warnings, ansi_padding, concat_null_yields_null, arithabort on;
set xact_abort, implicit_transactions, numeric_roundabort off;


declare @XTran bit = cast(sign(@@trancount) as bit);

begin try

if @XTran = 0
    begin tran;


-- Put your code here


if @XTran = 0
    commit;

end try
begin catch

if nullif(@Error, 0) is null
    select @Error = error_number(), @Message = error_message();

if @@trancount > 0 and @XTran = 0
    rollback;

end catch;
return;
go

有人可能会争辩说,显式发出set xact_abort off 可能会导致一些令人不快的副作用,例如批处理终止错误(例如208)会跳过catch 并使当前事务处于打开状态。这取决于你;这里的权衡是:

  • 更好的诊断。当数据库中的所有存储过程都遵循此模板时,它们会通过输出参数将错误冒泡到最外层的过程并优雅地回滚所有内容。
  • 出错后继续执行的可能性。例如,在事务回滚后记录错误,并确保日志记录不会随着事务的其余部分消失。

【讨论】:

  • @MartinSmith,很有趣。是的,我从来没有深入研究过这方面的细节,对我使用的方法感到满意,但它不会危及死锁监视器吗?假设我在catch 中有一个长时间运行的代码(CLR 函数将错误报告给外部服务,假设它是允许的)。上次我检查时,事务获取的锁以一种或另一种方式保持到结束。因此,在这种情况下,潜在的死锁解决方案并不能真正解决任何问题——获胜者还需要等待吗?
  • 确切地说,将@@transcount直接存储在begin transaction之后的变量中,然后回滚,直到@@trancount与存储的相同价值?因为我的过程可能会被另一个调用,然后当我进入我的过程时,@@transcount 已经是 1。
  • 是的,它可能会危及死锁处理。如果CATCH 块在回滚 tran 之前做了一些时间密集的事情,那么它将在释放锁之前增加一个延迟时间 - 但它现在只是阻塞而不是死锁,因为当事务最终完成时,情况会得到解决
  • @askolotl,这正是我的模板中 @XTran 变量的用途。当过程检测到外部事务时,它不会尝试对它做任何事情,将最终解决方案留给最外层(无论是入口点过程还是客户端)。
猜你喜欢
  • 2018-05-01
  • 2017-01-12
  • 2019-11-25
  • 2015-10-14
  • 2016-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多