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

odi*_*seh 166 sql sql-server

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

Ben*_*old 215

SET XACT_ABORT ON指示SQL Server回滚整个事务并在发生运行时错误时中止批处理.它涵盖了在客户端应用程序上发生的命令超时而不是SQL Server本身(默认XACT_ABORT OFF设置未涵盖)的情况.

由于查询超时将使事务保持打开状态,SET XACT_ABORT ON因此建议在具有显式事务的所有存储过程中使用(除非您有特定的理由),因为应用程序在与开放事务的连接上执行工作的后果是灾难性的.

Dan Guzman博客上有一个很棒的概述,

  • 那为什么默认情况下它不是ON? (30认同)
  • 如果您在 Sql 中有带有“BEGIN CATCH”块的“BEGIN TRY”-“BEGIN CATCH”和“ROLLBACK”,还需要 XACT_ABORT 吗? (3认同)
  • @user20358 `BEGIN TRY`-`BEGIN CATCH` 不会捕获客户端应用程序上发生的超时之类的事情,并且某些 SQL 错误也是无法捕获的,从而给您留下一个您不希望出现的开放事务。 (3认同)

Rem*_*anu 35

在我看来,SET XACT_ABORT ON在SQL 2k5中添加了BEGIN TRY/BEGIN CATCH已经过时了.在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
Run Code Online (Sandbox Code Playgroud)

它允许我编写原子过程,只有在可恢复错误的情况下回滚它们自己的工作.

Transact-SQL程序面临的主要问题之一是数据纯度:有时接收的参数或表中的数据都是完全错误的,导致重复的键错误,参考约束错误,检查约束错误等等.毕竟,这正是这些约束的作用,如果这些数据纯度错误是不可能的并且都被业务逻辑所捕获,则约束将全部过时(为效果添加了戏剧性的夸张).如果XACT_ABORT为ON,那么所有这些错误都会导致整个事务丢失,而不能正常编写处理异常的异常块.一个典型的例子是尝试执行INSERT并在PK违规时恢复为UPDATE.

  • 除了客户端超时...我的观点是SET XACT_ABORT在SQL 2005中更有效,因为行为更可预测:批量中止错误的数量要少得多. (7认同)
  • 我有点同意,但我计划围绕所有可能性进行错误处理,因为我知道如果发生命令超时,我将责备开发人员DBA. (6认同)
  • @RemusRusanu你还有什么办法可以处理长时间运行的同步数据库操作? (4认同)
  • MSDN文档指出:"对于大多数OLE DB提供程序(包括SQL Server),隐式或显式事务中的数据修改语句必须设置为XACT_ABORT.唯一不需要此选项的情况是提供程序是否支持嵌套事务." https://msdn.microsoft.com/en-us/library/ms188792(v=sql.120).aspx (4认同)
  • 客户端*超时*是应用程序错误.任何app都不应该强制命令超时.是这样一个令人震惊的糟糕做法,默认的SqlClient超时让我头疼每次我想他们怎么能运送那样的东西.您可以针对超时问题指导整个Transact-SQL错误处理,也可以正确编写客户端代码并解决错误处理中的业务问题.当然,我的观点来自开发人员的观点.admin/dba角色中的人通常会以不同的方式查看世界,因为他们会被其他创建的应用所困扰. (2认同)
  • "在我看来,SET XACT_ABORT ON被添加了BEGIN TRY/BEGIN CATCH过时了" - 我听到了,但请看http://www.sommarskog.se/error_handling/Part1.html (2认同)
  • TRY...CATCH 与 XACT_ABORT 行为列表 http://sommarskog.se/error_handling/Part2.html#classification (2认同)
  • *“任何应用程序都不应该强加命令超时。”* 我已经等了 9 年零 4 个月,但我对第 10 年的感觉很好! (2认同)
  • “客户端超时是应用程序错误。” 我不在乎这是谁的错误。无论为什么发生故障,我的数据都必须保持一致。 (2认同)

Vla*_*adV 21

引用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'
Run Code Online (Sandbox Code Playgroud)

此代码将在XACT_ABORT OFF时"成功"执行,并在XACT_ABORT为ON时终止,错误将终止("INSERT INTO t2"将不会执行,客户端应用程序将引发异常).

作为一种更灵活的方法,您可以在每个语句(旧学校)之后检查@@ ERROR,或使用TRY ... CATCH块(MSSQL2005 +).我个人更喜欢在没有理由进行某些高级错误处理时设置XACT_ABORT.


小智 6

关于客户端超时和使用XACT_ABORT来处理它们,我认为至少有一个很好的理由使客户端API(例如SqlClient)发生超时,这是为了防止客户端应用程序代码发生SQL Server代码中的死锁。在这种情况下,客户端代码没有错误,但是必须保护自己,使其永远不会阻塞,以等待命令在服务器上完成。反过来说,如果必须存在客户端超时来保护客户端代码,那么XACT_ABORT ON也必须保护服务器代码免受客户端中止的影响,以防服务器代码执行花费的时间超过客户端愿意等待的时间。