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

Tsql 分别在父/子存储过程中打开/关闭XACT_ABORT有什么影响?,tsql,error-handling,xact-abort,Tsql,Error Handling,Xact Abort,我试图改进当前系统的错误处理,以生成更有意义的错误消息。我有一个“root”存储过程,它对其他嵌套存储过程进行多次调用 在根sp中,XACT\u ABORT设置为ON,但在嵌套过程中,XACT\u ABORT设置为OFF。我希望从较低级别的过程中捕获特定的错误,而不是获取根过程的错误 我经常看到错误,在批处理结束时检测到不可提交的事务,事务正在回滚。 将这些“混合”环境与XACT\u中止一起使用是否有任何影响 此外,如果您对高级错误处理有任何建议,将不胜感激。我想我想使用sp_executesq

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

在根sp中,
XACT\u ABORT
设置为
ON
,但在嵌套过程中,
XACT\u ABORT
设置为
OFF
。我希望从较低级别的过程中捕获特定的错误,而不是获取根过程的错误

我经常看到错误,
在批处理结束时检测到不可提交的事务,事务正在回滚。

将这些“混合”环境与
XACT\u中止
一起使用是否有任何影响

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

根据和

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

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

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

请注意,
XACT\u ABORT
不是一个灵丹妙药,但是-例如,已经发生的错误不会终止批处理。然而,这似乎随着


正如您所建议的,并且根据,结构化异常处理(TRY/CATCH)是处理异常的一种更干净、更健壮的机制。

在调用可能调用其他SP的SP时,保持XACT_中止并获取错误(如果有)或提交(如果一切正常):例如两个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

谢谢你,斯图尔特。如果我在所有过程中都启用了XACT_ABORT,我假设这将影响当前设置为OFF的过程中的错误处理,是吗?另外,在XACT_ABORT开启时处理错误的最佳方法是什么,以获取特定的错误消息,因为产生错误的行后面的任何代码都不会执行?我相信CATCH块取代了XACT_ABORT,所以我必须在那里进行任何错误处理,然后回滚吗?这里的最终目标是从发生错误的实际过程中生成错误消息,而且不要让它在实际错误消息可能被更改的地方冒泡。理想情况下,IMO SQL结构化异常处理应该像其他语言一样运行,也就是说,如果不打算处理异常,就不要捕获异常。遗憾的是,SQL没有用于清理的
FINALLY
子句。这在某种程度上得到了缓解,例如XACT#U ABORT清理事务,而像#temp表之类的东西不需要显式删除,我发现只有少数实例需要捕获,只是为了清理(例如游标)。一个免责声明-我更多的是一个.NET应用程序开发人员,因此避免大量存储过程嵌套,因此我倾向于在应用程序中做更多的工作(例如使用TransactionScope等)