Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 2008 TSQL过程中的错误处理_Sql Server 2008_Sql Server 2005_Tsql_Error Handling_Service Broker - Fatal编程技术网

Sql server 2008 TSQL过程中的错误处理

Sql server 2008 TSQL过程中的错误处理,sql-server-2008,sql-server-2005,tsql,error-handling,service-broker,Sql Server 2008,Sql Server 2005,Tsql,Error Handling,Service Broker,问题摘要: 我所做的错误处理似乎过于复杂,仍然不能解决所有情况(因为可能存在事务处于不可模仿状态的情况)。我怀疑我: 错过了一些重要的事情,并且做错了(你能解释一下什么?我该怎么做吗?) 并没有遗漏任何东西——只是必须接受错误处理在SQL Server中仍然是一个巨大的问题 您能否提供更好的解决方案(针对以下情况)? 关于我的情况: 我在SQLServer中有(两个)存储过程,它们是从不同的地方调用的。可以概括为两种情况: 过程是从.NET代码调用的,事务是在SQL过程中生成和处理的 过程在

问题摘要:

我所做的错误处理似乎过于复杂,仍然不能解决所有情况(因为可能存在事务处于不可模仿状态的情况)。我怀疑我:

  • 错过了一些重要的事情,并且做错了(你能解释一下什么?我该怎么做吗?)
  • 并没有遗漏任何东西——只是必须接受错误处理在SQL Server中仍然是一个巨大的问题
您能否提供更好的解决方案(针对以下情况)?

关于我的情况:

我在SQLServer中有(两个)存储过程,它们是从不同的地方调用的。可以概括为两种情况:

  • 过程是从.NET代码调用的,事务是在SQL过程中生成和处理的
  • 过程在其他过程中调用(更具体地说,在ServiceBroker激活过程中),因此事务由外部过程处理
  • 我这样做的,过程返回结果(1表示成功,0表示失败)+在出错时返回用于日志记录的消息

    在程序内部:

    • 将XACT_中止设置为ON;——事务不会因为触发器而变得不可模仿
    • 声明@PartOfTran位=0;--用于保存状态:1-如果此过程是其他事务的一部分,或0-应启动新事务
    • 如果这是其他tran的一部分,则进行保存点。如果不是,则开始事务处理
    • 开始尝试块-执行所有操作,如果没有错误,并且这不是嵌套事务,则执行提交。如果是嵌套事务,则将在调用程序中进行提交
    • 如果出现错误:如果这是嵌套事务且事务处于可提交状态,则可以回滚到保存点“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
    
    其他文献:

    从我最喜欢的博客中可以看到:

  • -但我不喜欢“outer\u sp”有一行“IF@@trancount>0 ROLLBACK TRANSACTION”,因为在我的情况下-outer过程可以在事务中调用,所以在这种情况下我有“EXECUTE后的事务计数表示BEGIN和COMMIT语句的数量不匹配。上一个计数=1,当前计数=0。”
  • -实际上几乎和我在这里写的一样(也许这个想法来自于那篇博文-我根据我所读到的关于这个主题的所有内容来写我的解决方案)。这篇博文仍然无法解决如何处理不可模仿的事务。这是ServiceBroker的问题。如果我必须回滚不可模仿的事务,如何正确记录错误消息?我对此有一些想法,但所有这些似乎都是解决办法,而不是优雅的解决方案 在传输的末尾插入

     GOTO _SUCCESS
    
    IF @@ERROR > 0
            GOTO _FAIL
    
    因此,如果没有遇到任何错误,它将提交tran

    以及tran insert中的所有insert或update语句

     GOTO _SUCCESS
    
    IF @@ERROR > 0
            GOTO _FAIL
    
    所以当它出错的时候。。它将被送往医院

       _FAIL:
            ROLLBACK TRAN
            SET @ReturnCode = 1
            RETURN
    
    你可以在那里设置你所有的返回值

    在传输的末尾插入

     GOTO _SUCCESS
    
    IF @@ERROR > 0
            GOTO _FAIL
    
    因此,如果没有遇到任何错误,它将提交tran

    以及tran insert中的所有insert或update语句

     GOTO _SUCCESS
    
    IF @@ERROR > 0
            GOTO _FAIL
    
    所以当它出错的时候。。它将被送往医院

       _FAIL:
            ROLLBACK TRAN
            SET @ReturnCode = 1
            RETURN
    

    你可以在那里设置所有你的返回值

    在任何情况下,你都无法实现只回滚在
    usp\u MyProcedure
    中完成的工作的解决方案。考虑最明显的例子:死锁。当您收到异常1205的通知(您被选为死锁受害者)时,事务已经回滚(以允许进行)。随着错误处理的进行,唯一安全的选择是进一步引发并重新抛出,以便调用方有机会做出反应。“未提交事务”只是该主题的一个变体:当调用方启动事务时,错误处理无法以对调用方合理的方式从这种情况中恢复。最好的办法是举起(再扔)。这就是为什么我使用了你在我的博客中看到的模式

    在ServiceBroker上下文中考虑这一点意味着没有完全防弹的、异常安全的消息处理例程。如果遇到不可模仿的事务(或在处理catch块时已回滚的事务,如1205 deadlock),则必须回滚整批接收到的消息。在这种情况下,日志记录通常在最外层的捕捉块(通常位于激活程序中)之后进行。下面是如何工作的伪代码:

    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
    

    在任何情况下,您都无法实现只回滚在
    usp\u MyProcedure
    中完成的工作的解决方案。考虑最明显的例子:死锁。当您收到异常1205的通知(您被选为死锁受害者)时,事务已经回滚(以允许进行)。随着错误处理的进行,唯一安全的选择是进一步引发并重新抛出,以便调用方有机会做出反应。“未提交事务”只是该主题的一个变体:当调用方启动事务时,错误处理无法以对调用方合理的方式从这种情况中恢复。最好的办法是举起(再扔)。这就是为什么我使用了你在我的博客中看到的模式

    在ServiceBroker上下文中考虑这一点意味着没有完全防弹的、异常安全的消息处理例程。如果遇到不可模仿的事务(或在处理catch块时已回滚的事务,如1205 deadlock),则必须回滚整批接收到的消息。在这种情况下,日志记录通常在最外层的捕捉块(通常位于激活程序中)之后进行。下面是如何工作的伪代码:

    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
    
    嗯,这是一个旧猪圈