6

我正在尝试改进当前系统的错误处理以产生更有意义的错误消息。我有一个“根”存储过程,可以多次调用其他嵌套存储过程。

在根 sp 中,XACT_ABORT设置为 ,ON但在嵌套过程中,XACT_ABORT设置为OFF。我想从较低级别的程序中捕获特定错误,而不是获取根程序的错误。

我经常看到错误,uncommittable transaction is detected at the end of the batch, the transaction is being rolled back.

将这些“混合”环境与XACT_ABORTs?

此外,如果您对高级错误处理有任何建议,我们将不胜感激。我想我想使用sp_executesql这样我就可以传递参数来获取错误输出,而无需修改所有存储过程并使用它RAISERROR来调用父过程的CATCH块。

4

2 回答 2

5

根据Andomar 在此处MSDN 的回答:

SET XACT_ABORT 的设置是在执行或运行时设置的,而不是在解析时设置的

XACT_ABORT不会从创建会话“复制”到每个过程,因此任何未在内部显式设置此选项的 PROC 将在运行时从环境会话继承设置,这可能是灾难性的。

FWIW,作为一般规则,我们始终确保XACT_ABORT全局开启并进行 lint 检查以确保我们的任何 PROC 都没有覆盖此设置。

但是请注意,这XACT_ABORT不是灵丹妙药 - 例如,您的 PROC 使用 RAISERROR 引发的错误不会终止批处理。但是,这似乎通过SQL 2012 中的 THROW 关键字得到了改进

正如您所建议的那样,并且根据Remus Rusanu 的观察,结构化异常处理 (TRY / CATCH) 是一种更加干净和健壮的异常处理机制。

于 2012-11-09T11:05:29.673 回答
1

一种在调用可能调用其他 SP 的 SP 时保持 XACT_ABORT 开启并获取错误(如果有)或提交(如果一切正常)的方法:例如两个 sp 和三个测试

 create PROCEDURE [dbo].[myTestProcCalled]
    (
        @testin int=0
    )
    as
    begin

        declare @InnerTrans int 
        set XACT_ABORT on;



        set @InnerTrans = @@trancount;

        PRINT '02_01_Trancount='+cast (@InnerTrans as varchar(2));

        begin try
           if (@InnerTrans = 0)
           begin
                PRINT '02_02_beginning trans';
                begin transaction
           end 

           declare @t2 int
           set @t2=0
           PRINT '02_03_doing division'
           set @t2=10/@testin 

           PRINT '02_04_doing AfterStuff'
           if (@InnerTrans = 0 and XACT_STATE()=1)
             begin
                 PRINT '02_05_Committing'

                commit transaction
              end
              PRINT '02_05B_selecting calledValue=' +cast(@t2 as varchar(20))
              select  @t2 as insidevalue

        end try

        begin catch
         PRINT '02_06_Catching Errors from called'
              declare @ErrorMessage nvarchar(4000);
            declare @ErrorNumber int;
            declare @ErrorSeverity int;
            declare @ErrorState int;

            select @ErrorMessage = error_message(), @ErrorNumber = error_number(), @ErrorSeverity = error_severity(), @ErrorState = error_state();

            if (@InnerTrans = 0 and XACT_STATE()=-1)
            begin
              PRINT '02_07_Rolbacking'
              rollback transaction
            end
           PRINT '02_08_Rising Error'
            raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState); 
            --use throw if in 2012 or above 
            -- else might add a "return" statement
        end catch

    end
    go

    create PROCEDURE [dbo].[myTestPCalling]
    (
        @test int=0
        ,@testinside int=0
    )
    as
    begin

        declare @InnerTrans int 
        set XACT_ABORT on;



        set @InnerTrans = @@trancount;

        PRINT '01_01_Trancount='+cast (@InnerTrans as varchar(2));

        begin try
           if (@InnerTrans = 0)
           begin
                PRINT '01_02_beginning trans';
                begin transaction
           end 

           declare @t2 int
           set @t2=0
           PRINT '01_03_doing division'
           set @t2=10/@test 
           PRINT '01_04_calling inside sp'
           execute [dbo].[myTestProcCalled]
                  @testin  = @testinside
           --
           PRINT '01_05_doing AfterStuff'
           if (@InnerTrans = 0 and XACT_STATE()=1)
             begin
                 PRINT '01_06_Committing'
                 commit transaction
                 PRINT '01_06B_selecting callerValue=' +cast(@t2 as varchar(20))
              select  @t2 as outsidevalue

              end
        end try

        begin catch
         PRINT '01_07_Catching Errors from Caller'
            declare @ErrorMessage nvarchar(4000);
            declare @ErrorNumber int;
            declare @ErrorSeverity int;
            declare @ErrorState int;

            select @ErrorMessage = error_message(), @ErrorNumber = error_number(), @ErrorSeverity = error_severity(), @ErrorState = error_state();
            if (@InnerTrans = 0 and XACT_STATE()=-1)
            begin
              PRINT '01_08_Rolbacking'
              rollback transaction
            end
            PRINT '01_09_Rising Error'
            raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState); 
            --use throw if in 2012 or above 
            -- else might add a "return" statement
        end catch

    end

----test 1 :result OK----
USE [PRO-CGWEB]
GO

DECLARE @return_value int

EXEC    @return_value = [dbo].[myTestPCalling]
        @test =2
        ,@testinside = 2

SELECT  'Return Value' = @return_value

GO

----test2 :error in caller ----
USE [PRO-CGWEB]
GO

DECLARE @return_value int

EXEC    @return_value = [dbo].[myTestPCalling]
        @test =0
        ,@testinside = 2

SELECT  'Return Value' = @return_value

GO

----test3 :error in calling ----
USE [PRO-CGWEB]
GO

DECLARE @return_value int

EXEC    @return_value = [dbo].[myTestPCalling]
        @test =2
        ,@testinside = 0

SELECT  'Return Value' = @return_value

GO
于 2016-10-04T12:43:26.637 回答