sql-server 在存储过程中使用“SET XACT_ABORT ON”有什么好处?

svgewumm  于 2022-10-31  发布在  其他
关注(0)|答案(6)|浏览(226)

在存储过程中使用**SET XACT_ABORT ON**有什么好处?

qnyhuwrf

qnyhuwrf1#

SET XACT_ABORT ON指示SQL Server在发生运行时错误时回滚整个事务并中止批处理。它适用于在客户端应用程序上而不是在SQL Server本身发生命令超时的情况(默认的XACT_ABORT OFF设置不适用于这种情况)。
由于查询超时将使事务保持打开状态,因此建议在所有具有显式事务的存储过程中使用SET XACT_ABORT ON(除非您有特殊的理由不这样做),因为应用程序在具有打开事务的连接上执行工作的后果是灾难性的。
Dan Guzman's Blog上有一个非常棒的概述,

nbewdwxp

nbewdwxp2#

在我看来,SQL 2k 5中添加的开始TRY/BEGIN CATCH使SET XACT_ABORT ON过时了。在Transact-SQL中出现异常块之前,处理错误确实很困难,而且不平衡的过程太常见了(与入口相比,在出口处具有不同的@@TRANCOUNT的过程)。
有了Transact-SQL异常处理的加入,编写保证正确平衡事务的正确过程就容易多了。

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end
go

它允许我编写原子过程,在出现可恢复错误的情况下,只回滚自己的工作。
Transact-SQL过程面临的主要问题之一是数据纯度:有时接收到的参数或表中的数据完全是错误的,导致重复键错误、引用约束错误、检查约束错误等等。毕竟,这正是这些约束的作用,如果这些数据纯度错误不可能出现,并且都被业务逻辑捕获,这些约束都将被废弃如果XACT_ABORT为ON,则所有这些错误都将导致整个事务丢失,而不是编写异常块来优雅地处理异常。一个典型的例子是尝试执行INSERT并在PK冲突时恢复到UPDATE。

t30tvxxf

t30tvxxf3#

报价MSDN
当SET XACT_ABORT为ON时,如果Transact-SQL语句引发运行时错误,则终止并回滚整个事务。当SET XACT_ABORT为OFF时,在某些情况下,只回滚引发错误的Transact-SQL语句,而事务继续处理。
实际上,这意味着某些语句可能会失败,使事务“部分完成”,并且调用方可能没有失败的迹象。
举个简单的例子:

INSERT INTO t1 VALUES (1/0)    
INSERT INTO t2 VALUES (1/1)    
SELECT 'Everything is fine'

XACT_ABORT为OFF时,此代码将“成功”执行,XACT_ABORT为ON时,此代码将因错误而终止(“INSERT INTO t2”将不会执行,并且客户端应用程序将引发异常)。
作为一种更灵活的方法,您可以在每个语句后检查@@ERROR(老方法),或者使用TRY... CATCH块(MSSQL 2005+)。就我个人而言,只要没有必要进行某些高级错误处理,我就更喜欢将XACT_ABORT设置为ON。

k7fdbhmy

k7fdbhmy4#

关于客户端超时和使用XACT_ABORT来处理它们,在我看来,至少有一个很好的理由在SqlClient这样的客户端API中使用超时,那就是保护客户端应用程序代码不受SQL Server代码中发生的死锁的影响。在这种情况下,客户端代码没有错误,但必须保护自己不被永远阻塞,以等待服务器上的命令完成。因此,相反,如果必须存在客户端超时来保护客户端代码,那么XACT_ABORT ON也必须保护服务器代码不受客户端异常终止的影响,以防服务器代码的执行时间比客户端愿意等待的时间长。

bjp0bcyl

bjp0bcyl5#

它用于事务管理,以确保任何错误都会导致事务回滚。

ippsafx7

ippsafx76#

在此处添加新的更新。最新的MSDN更新显示了如何使用XACT_ABORT ON和TRY/CATCH块。MSDN Link

-- Check to see whether this stored procedure exists.  
IF OBJECT_ID (N'usp_GetErrorInfo', N'P') IS NOT NULL  
    DROP PROCEDURE usp_GetErrorInfo;  
GO  

-- Create procedure to retrieve error information.  
CREATE PROCEDURE usp_GetErrorInfo  
AS  
    SELECT   
         ERROR_NUMBER() AS ErrorNumber  
        ,ERROR_SEVERITY() AS ErrorSeverity  
        ,ERROR_STATE() AS ErrorState  
        ,ERROR_LINE () AS ErrorLine  
        ,ERROR_PROCEDURE() AS ErrorProcedure  
        ,ERROR_MESSAGE() AS ErrorMessage;  
GO  

-- SET XACT_ABORT ON will cause the transaction to be uncommittable  
-- when the constraint violation occurs.   
SET XACT_ABORT ON;  

BEGIN TRY  
    BEGIN TRANSACTION;  
        -- A FOREIGN KEY constraint exists on this table. This   
        -- statement will generate a constraint violation error.  
        DELETE FROM Production.Product  
            WHERE ProductID = 980;  

    -- If the DELETE statement succeeds, commit the transaction.  
    COMMIT TRANSACTION;  
END TRY  
BEGIN CATCH  
    -- Execute error retrieval routine.  
    EXECUTE usp_GetErrorInfo;  

    -- Test XACT_STATE:  
        -- If 1, the transaction is committable.  
        -- If -1, the transaction is uncommittable and should   
        --     be rolled back.  
        -- XACT_STATE = 0 means that there is no transaction and  
        --     a commit or rollback operation would generate an error.  

    -- Test whether the transaction is uncommittable.  
    IF (XACT_STATE()) = -1  
    BEGIN  
        PRINT  
            N'The transaction is in an uncommittable state.' +  
            'Rolling back transaction.'  
        ROLLBACK TRANSACTION;  
    END;  

    -- Test whether the transaction is committable.
    -- You may want to commit a transaction in a catch block if you want to commit changes to statements that ran prior to the error.
    IF (XACT_STATE()) = 1  
    BEGIN  
        PRINT  
            N'The transaction is committable.' +  
            'Committing transaction.'  
        COMMIT TRANSACTION;     
    END;  
END CATCH;  
GO

相关问题