¿Cuál es el beneficio de usar "SET XACT_ABORT ON" en un procedimiento almacenado?

Respuestas:

231

SET XACT_ABORT ONindica a SQL Server que revierta toda la transacción y cancele el lote cuando se produce un error en tiempo de ejecución. Te cubre en casos como un tiempo de espera de comando que ocurre en la aplicación cliente en lugar de dentro del propio SQL Server (que no está cubierto por la XACT_ABORT OFFconfiguración predeterminada ).

Dado que un tiempo de espera de consulta dejará abierta la transacción, SET XACT_ABORT ONse recomienda en todos los procedimientos almacenados con transacciones explícitas (a menos que tenga una razón específica para hacerlo de otra manera) ya que las consecuencias de que una aplicación realice un trabajo en una conexión con una transacción abierta son desastrosas.

Hay una gran visión general en el blog de Dan Guzman ,

Ben Griswold
fuente
41
Entonces, ¿por qué no está ENCENDIDO por defecto?
Mike W
1
¿Todavía se requiere XACT_ABORT si tiene el BEGIN TRY- BEGIN CATCHy ROLLBACKcon el BEGIN CATCHbloque en SQL?
user20358
1
@ user20358 BEGIN TRY: BEGIN CATCHno detectará cosas como un tiempo de espera en la aplicación cliente, y algunos errores de SQL también son inadmisibles, dejándolo con una transacción abierta donde no esperaría uno.
Tom Lint el
37

En mi opinión, SET XACT_ABORT ON quedó obsoleto al agregar BEGIN TRY / BEGIN CATCH en SQL 2k5. Antes de los bloques de excepción en Transact-SQL, era realmente difícil manejar los errores y los procedimientos desequilibrados eran demasiado comunes (los procedimientos que tenían un @@ TRANCOUNT diferente en la salida en comparación con la entrada).

Con la adición del manejo de excepciones Transact-SQL es mucho más fácil escribir procedimientos correctos que garanticen un equilibrio adecuado de las transacciones. Por ejemplo, uso esta plantilla para el manejo de excepciones y transacciones anidadas :

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

Me permite escribir procedimientos atómicos que revierten solo su propio trabajo en caso de errores recuperables.

Uno de los principales problemas que enfrentan los procedimientos de Transact-SQL es la pureza de los datos : a veces los parámetros recibidos o los datos en las tablas son simplemente incorrectos, lo que resulta en errores de clave duplicados, errores de restricción de referencia, errores de restricción de verificación y así sucesivamente. Después de todo, ese es exactamente el papel de estas restricciones, si estos errores de pureza de datos fueran imposibles y todos atrapados por la lógica del negocio, las restricciones serían completamente obsoletas (se agrega una exageración dramática para que tenga efecto). Si XACT_ABORT está ENCENDIDO, todos estos errores dan como resultado la pérdida de toda la transacción, en lugar de poder codificar bloques de excepción que manejan la excepción con gracia. Un ejemplo típico es tratar de hacer una INSERTAR y volver a una ACTUALIZACIÓN en violación de PK.

Remus Rusanu
fuente
9
Excepto por los tiempos de espera del cliente ... y mi opinión es que SET XACT_ABORT es más efectivo en SQL 2005 porque el comportamiento es más predecible: muchos menos errores de aborto por lotes.
gbn
77
Estoy un poco de acuerdo, pero planeo mi manejo de errores en torno a todas las eventualidades, porque sé que tendré la culpa como DBA de desarrollador si se produce un tiempo de espera de comando.
gbn
44
@RemusRusanu ¿De qué otra forma manejarías una operación de base de datos síncrona y de larga ejecución?
Ian Boyd
55
La documentación de MSDN dice: "XACT_ABORT debe estar ACTIVADO para las declaraciones de modificación de datos en una transacción implícita o explícita contra la mayoría de los proveedores OLE DB, incluido SQL Server. El único caso en el que esta opción no es necesaria es si el proveedor admite transacciones anidadas". msdn.microsoft.com/en-us/library/ms188792(v=sql.120).aspx
Nathan
44
"En mi opinión, SET XACT_ABORT ON quedó obsoleto por la adición de BEGIN TRY / BEGIN CATCH" - Te escucho, pero por favor mira sommarskog.se/error_handling/Part1.html
Ingeniero invertido
22

Citando MSDN :

Cuando SET XACT_ABORT está ENCENDIDO, si una instrucción Transact-SQL genera un error de tiempo de ejecución, la transacción completa se termina y se revierte. Cuando SET XACT_ABORT está desactivado, en algunos casos solo se revierte la instrucción Transact-SQL que provocó el error y la transacción continúa procesándose.

En la práctica, esto significa que algunas de las declaraciones pueden fallar, dejando la transacción 'parcialmente completada', y puede que no haya señales de esta falla para la persona que llama.

Un simple ejemplo:

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

Este código se ejecutaría 'exitosamente' con XACT_ABORT OFF, y terminará con un error con XACT_ABORT ON ('INSERT INTO t2' no se ejecutará, y una aplicación cliente generará una excepción).

Como un enfoque más flexible, puede verificar @@ ERROR después de cada declaración (vieja escuela), o usar TRY ... CATCH blocks (MSSQL2005 +). Personalmente, prefiero configurar XACT_ABORT ON siempre que no haya razón para un manejo avanzado de errores.

VladV
fuente
8

Con respecto a los tiempos de espera de los clientes y el uso de XACT_ABORT para manejarlos, en mi opinión hay al menos una muy buena razón para tener tiempos de espera en las API de clientes como SqlClient, y es para proteger el código de la aplicación del cliente de los puntos muertos que se producen en el código del servidor SQL. En este caso, el código del cliente no tiene fallas, pero tiene que protegerse a sí mismo del bloqueo para siempre esperando que el comando se complete en el servidor. Por el contrario, si los tiempos de espera del cliente tienen que existir para proteger el código del cliente, XACT_ABORT ON también debe proteger el código del servidor de los abortos del cliente, en caso de que el código del servidor tarde más en ejecutarse de lo que el cliente está dispuesto a esperar.

ionutm
fuente
1

Se utiliza en la gestión de transacciones para garantizar que cualquier error provoque que la transacción se revierta.

Dan Diplo
fuente