【问题标题】:Cannot roll back subtransaction. No transaction or savepoint of that name was found无法回滚子事务。未找到该名称的事务或保存点
【发布时间】:2013-10-08 23:02:46
【问题描述】:

我在 sql 中有一个循环,它会做一些事情

    begin tran one

    do some inserts in others tables

      --start loop
     begin tran two
      --do something
      begin try
--if something fail then a trigger does rollback and this return a error (and this goes to catch), then don't i need do the rollbak in catch? this could not be dissable because this is working on production
      --something finished ok 
      commit tran two
      end try
      begin catch
     rollback tran two
      end catch

    --finished loop
    commit


----------

我收到了这个错误

在批处理结束时检测到不可提交的事务。这 事务被回滚。

begin tran one
begin tran two

rollback tran two

做这个代码我得到这个:

不能回滚两个。未找到该名称的事务或保存点。

我只希望子查询回滚第二个循环并继续处理其他记录。

【问题讨论】:

标签: sql tsql loops transactions


【解决方案1】:

操作员回滚回滚所有事务,对于仅回滚第二个循环,您必须使用保存点:

  begin tran one

-- do some inserts in others tables

  --start loop
  save tran two -- begin tran two

  --do something
  begin try
     update product set id = 1 --if something fail then a trigger does rollback and this return a error (and this goes to catch), then don't i need do the rollbak in catch? this could not be dissable because this is working on production

  --something finished ok 
  commit tran two
  end try
  begin catch

    rollback tran two
  end catch

--finished loop
commit

触发示例:

create table product (id int)
GO  
create trigger product_trigger on product for update
as
  set xact_abort off

  if (select count(*) from inserted i join product p on i.id=p.id)=0 begin 
    if (@@trancount>0) begin 
      /* rollback */ 
      raiserror('product does not exist', 16, 1) 
    end 
  end

【讨论】:

  • 好吧,如果我可以重命名第一个事务,这没关系,但正如我所说,有一个触发器可以回滚,然后我无法修改它,因为这是在生产中,而且这只回滚,如果这确实回滚一个,这可能会起作用,但这只会回滚,然后这段代码不起作用。
  • if(select count(*) from inserted i join product p on i.id=p.id)=0 begin if(@@trancount>0) begin rollback raiseerror('产品不存在') 结束结束
  • 我添加了一个触发器的例子
【解决方案2】:

在我的例子中,我的代码是否通过 EF DbContext 方法调用了一个包含非嵌套事务的 SQL Server 存储过程。

因为,正如@NotMe 已经指出的那样,“there is no such as a nested transaction in SQL Server”,我开始怀疑我的流程是否真的是无事务嵌套的。

怀疑我的 DbContext 有点内疚,我开始检查 DbContext 选项,直到 DbContext.Configuration.EnsureTransactionsForFunctionsAndCommands = True 引起了我的注意。

所以,只要我将它的值更改为 True,一切都会成功。

MyDbContext.Configuration.EnsureTransactionsForFunctionsAndCommands = false;

发生了什么?

嗯,在我看来,EF 的 ObjectContext.ExecuteFunction 方法将其自己的外部事务作为我存储过程的内部事务的包装器来管理,因此,当我的存储过程的 ROLLBACK TRAN 被命中时,有当 EF 的 COMMIT/ROLLBACK 代码被命中时,没有待处理的事务。

奇怪的是,在收集有关 EnsureTransactionsForFunctionsAndCommands 属性的一些参考资料时,我发现这种默认行为是由于 EF 团队有史以来最糟糕的决定之一(在我看来),因为它直接与每个T-SQL 脚本中的 ROLLBACK TRAN。

有关 EF 的更多详细信息,请通过EF6 wraps every single stored procedure call in its own transaction. How to prevent this?查看insightfull SO 的质量保证

基本上,每个人都应该在发出 ROLLBACK 命令之前检查@@trancount > 0,无论是否命名,特别是在存储过程中。

CREATE PROCEDURE Proc1 AS
BEGIN
    BEGIN TRAN
    EXEC Proc2
    IF(@@trancount > 0)
        COMMIT TRAN
END

CREATE PROCEDURE Proc2 AS
BEGIN
    BEGIN TRAN
    ROLLBACK TRAN
END

为了更好地了解 Microsoft SQL Server 的嵌套事务,我建议阅读以下文章 Be careful using ROLLBACK on nested transaction in SQL Server!

希望它可以帮助某人:-)

【讨论】:

  • 在找到这个答案之前,我真的看错了方向。谢谢!
  • 那么如果我们的 orm 和存储过程有自己的嵌套事务,那么执行事务的正确方法是什么?我的 dapper/sql 客户端混合查询遇到了同样的问题。新查询依赖于 dapper,但所有旧代码仍然依赖于传统的 sql 客户端,当查询时间过长并对其他查询产生蝴蝶效应时会弹出此错误。 @Julio
  • @phonemyatt,虽然我从未使用过 Dapper,但在阅读了您的评论后,我刚刚意识到,如果我可以更深入地了解这个问题的根本原因以及如何克服它,为了为了解决它,我刚刚更新了我的答案。我建议你从“基本上……”开始阅读。
猜你喜欢
  • 2020-09-14
  • 1970-01-01
  • 1970-01-01
  • 2016-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-23
相关资源
最近更新 更多