¿Cuál es el beneficio de usar SET XACT_ABORT ON
en un procedimiento almacenado?
¿Cuál es el beneficio de usar SET XACT_ABORT ON
en un procedimiento almacenado?
Respuestas:
SET XACT_ABORT ON
indica 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 OFF
configuración predeterminada ).
Dado que un tiempo de espera de consulta dejará abierta la transacción, SET XACT_ABORT ON
se 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 ,
BEGIN TRY
- BEGIN CATCH
y ROLLBACK
con el BEGIN CATCH
bloque en SQL?
BEGIN TRY
: BEGIN CATCH
no 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.
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.
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.
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.