sql-server-2008 - SQL过程中的错误处理

标签 sql-server-2008 sql-server-2005 tsql error-handling service-broker

问题摘要:

我做了错误处理,看起来似乎太复杂了,仍然不能解决所有情况(因为在某些情况下,事务处于不可提交状态)。我怀疑我:

  • 错过了重要的事情并且做错了(您能解释什么?然后该怎么做?)。
  • 没有错过任何内容-只需接受错误处理仍然是SQL Server中的巨大问题。

  • 您能否提供更好的解决方案(针对下面描述的情况)?

    关于我的情况:

    我在SQL Server中有(一对)存储过程,这是从不同的地方调用的。可以概括为两种情况:
  • 从.NET代码中调用过程,在SQL过程
  • 中进行事务处理
  • 过程在其他过程中调用(更具体地说,在Service Broker激活过程中),因此事务由外部过程处理。

  • 我做到了,该过程返回结果(1表示成功,0表示失败)+在出现错误的情况下返回消息以进行记录。

    程序内部:
  • 将XACT_ABORT设置为ON; -交易不会因触发器而变得不可 promise 。
  • 声明@PartOfTran位= 0; -用于保存状态:1-如果此过程是其他事务的一部分,或者0-应该开始新的事务。
  • 如果这是其他版本的一部分,请保存点。如果不是,则开始交易。
  • 开始尝试阻塞-进行所有操作,如果没有错误,并且如果这不是嵌套事务,则提交。如果是嵌套的,事务将在调用者过程中进行提交。
  • 如果发生错误:如果是嵌套事务,并且事务处于可提交状态-可以回滚到保存点“MyTran”。如果它不是事务的一部分,则回滚事务称为“MyTran”。在所有其他情况下-仅返回错误代码和消息。

  • 代码如下:
    Create Procedure dbo.usp_MyProcedure 
    (
        -- params here ...
        @ReturnCode int out, -- 1 Success, != 1 Error
        @ReturnMsg nvarchar(2048) out
    )
    AS
    Begin
        Set NoCount ON;
        Set XACT_ABORT ON; 
    
        Declare @PartOfTran bit = 0;
    
        IF(@@TRANCOUNT > 0)
            Begin 
                SET @PartOfTran = 1;
                SAVE TRAN MyTran;
            END
        Else
            BEGIN TRAN MyTran;
    
        Begin Try
            -- insert table1
            -- update table2
            -- ....
    
            IF(@PartOfTran = 0) 
                COMMIT TRAN MyTran;
            Select @ReturnCode = 1, @ReturnMsg = Null;
        End Try
        Begin Catch
            IF (XACT_STATE() = 1 And @PartOfTran = 1) OR @PartOfTran = 0
                Rollback Tran MyTran;
            Select @ReturnCode = 0, @ReturnMsg = ERROR_MESSAGE();
        End Catch
    End
    

    其他文学:

    从我最喜欢的博客中看到:
  • sommarskog-但我不喜欢“outer_sp”具有“IF @@ trancount> 0 ROLLBACK TRANSACTION”行,因为在我的情况下,可以在事务中调用外部过程,因此在这种情况下,我有“EXECUTE之后的事务计数表示BEGIN和COMMIT语句的数量不匹配。上一个计数= 1,当前计数=0。”
  • rusanu-实际上与我在这里写的差不多(也许想法来自该博客帖子-我根据关于该主题的所有知识编写了自己的解决方案)。这篇博文仍然无法解决我无法处理的交易。对于Service Broker,这是个问题。如果必须回滚不可提交的事务,如何正确记录错误消息?我对此有想法,但所有这些想法似乎都是解决方法,而不是优雅的解决方案。
  • 最佳答案

    在任何情况下,您将无法实现仅回退usp_MyProcedure中完成的工作的解决方案。考虑最明显的例子:死锁。当您收到关于异常1205的通知(您被选为死锁受害者)时,事务已经回滚(以允许进度)。随着错误处理的进行,唯一安全的选择是进一步引发并重新抛出,以便调用者有机会使用react。 “不可提交的事务”只是该主题的一种变体:当调用者开始事务时,错误处理无法以对调用者敏感的方式从这种情况中恢复。最好的事情是提高(重新 throw )。这就是为什么我使用您在Exception HAndling and Nested Transactions博客中看到的模式的原因

    在Service Broker上下文中考虑这一点,意味着没有完全的项目符号证明,异常安全的消息处理例程。如果您遇到了不可提交的事务(或者在处理catch块时已经回滚的事务,例如1205死锁),那么您将不得不回滚整批收到的消息。在这种情况下,通常在最外面的catch块之后进行记录(通常在激活的过程中定位)。这是如何工作的伪代码:

    usp_myActivatedProc
    as
    @commited = false;
    @received = 0;
    @errors = 0;
    
    begin transaction
    begin try
      receive ... into @table;
      @received = @@row_count;
      foreach message in @table
        save transaction
        begin try
           process one message: exec usp_myProcedure @msg
        end try
        begin catch
          if xact_state()=1
            rollback to savepoint
            @errors += 1;
            -- decide what to do with failed message, log
            -- this failure may still be committed (receive won't roll back yet)
          else 
            -- this is a lost cause, re-throw
            raiserror
        end catch
        fetch next @table
      endfor
      commit
      @commited = true;
    end try
    catch
        @error_message = error_message();
        if xact_state() != 0
          rollback
    end catch
    if @commited = false
    begin
       insert into logging 'failed', @received, @error_message
    end
    

    关于sql-server-2008 - SQL过程中的错误处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9565250/

    相关文章:

    sql-server - SSIS - 如何使用结果集作为 SQL 任务的输入并获得正确的数据类型?

    sql-server-2008 - 如何在现有数据库中实现多态关联

    sql-server-2008 - 如何插入包含单引号字符的字符串?

    asp.net - 使用 ASN.NET 了解 SQL 中应用程序角色的性能影响

    xml - 使用新架构更新 XML 列架构

    sql - 为索引列表生成CREATE脚本

    sql - 在多个变量上使用 RANK 的快速帮助

    sql-server - WITH VIEW_METADATA 选项对 SQL Server 中的 View 有何作用?

    tsql - 如何从变量运行生成的 SQL?

    sql - 如何返回表的列名?