在父/子存储过程中分别启用/关闭XACT_ABORT有什么影响?

时间:2012-11-08 14:31:28

标签: tsql error-handling xact-abort

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

在根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块。 / p>

2 个答案:

答案 0 :(得分:5)

根据Andomar's answer hereMSDN:

  

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

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

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

请注意,XACT_ABORT不是银弹,例如 - 已经raised by your PROC with RAISERROR的错误不会终止批处理。但是,THROW keyword in SQL 2012

似乎改善了这一点

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

答案 1 :(得分:1)

一种保持XACT_ABORT并在任何情况下获取错误的方法或提交如果在调用可能调用其他SP的SP时一切正常:两个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