Solución:
Resulta que la transacción puede no comprometerse desde el interior del CATCH
bloquear si XACT_ABORT
se establece en ON
.
El ejemplo de MSDN es algo engañoso, porque la comprobación implica que XACT_STATE
puede devolver 1 en algunos casos y puede ser posible COMMIT
la transacción.
IF (XACT_STATE()) = 1
BEGIN
PRINT 'The transaction is committable.' +
' Committing transaction.'
COMMIT TRANSACTION;
END;
No es cierto, XACT_STATE
nunca volveré 1 adentro CATCH
bloquear si XACT_ABORT
se establece en ON
.
Parece que el código de muestra de MSDN estaba destinado a ilustrar principalmente el uso de XACT_STATE()
función independientemente de la XACT_ABORT
configuración. El código de muestra parece lo suficientemente genérico como para funcionar con ambos XACT_ABORT
ajustado a ON
y OFF
. Es solo que con XACT_ABORT = ON
el cheque IF (XACT_STATE()) = 1
se vuelve innecesario.
Hay un muy buen conjunto de artículos detallados sobre el manejo de errores y transacciones en SQL Server por Erland Sommarskog. En la Parte 2 – Clasificación de errores, presenta una tabla completa que reúne todas las clases de errores y cómo los maneja SQL Server y cómo TRY ... CATCH
y XACT_ABORT
cambia el comportamiento.
+-----------------------------+---------------------------++------------------------------+
| | Without TRY-CATCH || With TRY-CATCH |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| SET XACT_ABORT | OFF | ON | OFF | ON || ON or OFF | OFF | ON |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Class Name | Aborts | Rolls || Catchable | Dooms |
| | | Back || |transaction|
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Fatal errors | Connection | Yes || No | n/a |
| Batch-aborting | Batch | Yes || Yes | Yes |
| Batch-only aborting | Batch | No | Yes || Yes | No | Yes |
| Statement-terminating | Stmnt | Batch | No | Yes || Yes | No | Yes |
| Terminates nothing at all | Nothing | No || Yes | No | Yes |
| Compilation: syntax errors | (Statement) | No || Yes | No | Yes |
| Compilation: binding errors | Scope | Batch | No | Yes || Outer scope only | No | Yes |
| Compilation: optimisation | Batch | Yes || Outer scope only | Yes |
| Attention signal | Batch | No | Yes || No | n/a |
| Informational/warning msgs | Nothing | No || No | n/a |
| Uncatchable errors | Varying | Varying || No | n/a |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
La última columna de la tabla responde a la pregunta. Con TRY-CATCH
y con XACT_ABORT ON
la transacción está condenada en todos los casos posibles.
Una nota fuera del alcance de la pregunta. Como dice Erland, esta coherencia es una de las razones para establecer XACT_ABORT
para ON
:
Ya he dado la recomendación de que sus procedimientos almacenados deben incluir el comando
SET XACT_ABORT, NOCOUNT ON
. Si miras la tabla de arriba, verás que conXACT_ABORT
en efecto, hay un nivel más alto de consistencia. Por ejemplo, la transacción siempre está condenada al fracaso. A continuación, mostraré muchos ejemplos en los que establezcoXACT_ABORT
paraOFF
, para que pueda comprender por qué debe evitar esta configuración predeterminada.
TL; DR / Resumen ejecutivo: Con respecto a esta parte de la pregunta:
No veo en que casos se puede pasar el control por dentro
CATCH
con una transacción que se puede comprometer cuandoXACT_ABORT
se establece enON
.
He hecho bastantes pruebas sobre esto ahora y no puedo encontrar ningún caso en el que XACT_STATE()
devoluciones 1
dentro de un CATCH
bloquear cuando @@TRANCOUNT > 0
y la propiedad de sesión de XACT_ABORT
es ON
. Y de hecho, de acuerdo con la página actual de MSDN para SET XACT_ABORT:
Cuando SET XACT_ABORT está en ON, si una instrucción Transact-SQL genera un error en tiempo de ejecución, toda la transacción se termina y se revierte.
Esa declaración parece estar de acuerdo con sus especulaciones y mis hallazgos.
Artículo de MSDN sobre
SET XACT_ABORT
tiene un ejemplo cuando algunas declaraciones dentro de una transacción se ejecutan con éxito y algunas fallan cuandoXACT_ABORT
se establece enOFF
Cierto, pero las declaraciones en ese ejemplo son no Dentro de un TRY
cuadra. Esas mismas declaraciones dentro de un TRY
bloque aún evitaría la ejecución de cualquier declaración después de la que causó el error, pero suponiendo que XACT_ABORT
es OFF
, cuando el control se pasa al CATCH
bloquear la transacción sigue siendo físicamente válida en el sentido de que todos los cambios anteriores ocurrieron sin error y pueden Comprometerse, si ese es el deseo, o se pueden revertir. Por otro lado, si XACT_ABORT
es ON
luego, los cambios anteriores se revierten automáticamente y luego tiene la opción de: a) emitir un ROLLBACK
que es principalmente una aceptación de la situación, ya que la transacción ya fue revertida menos el reinicio @@TRANCOUNT
para 0
, o b) obtiene un error. No hay mucha elección, ¿verdad?
Un detalle posiblemente importante de este rompecabezas que no es evidente en esa documentación para SET XACT_ABORT
es que esta propiedad de sesión, e incluso ese código de ejemplo, ha existido desde SQL Server 2000 (la documentación es casi idéntica entre las versiones), anterior a la TRY...CATCH
constructo que se introdujo en SQL Server 2005. Mirando esa documentación nuevamente y mirando el ejemplo (sin los TRY...CATCH
), utilizando XACT_ABORT ON
causa un inmediato reversión de la Transacción: no hay un estado de Transacción de “no comprometible” (tenga en cuenta que no hay ninguna mención de un estado de Transacción “no comprometible” en ese SET XACT_ABORT
documentación).
Creo que es razonable concluir que:
- la introducción de la
TRY...CATCH
La construcción en SQL Server 2005 creó la necesidad de un nuevo estado de transacción (es decir, “no comprometible”) y elXACT_STATE()
función para obtener esa información. - comprobación
XACT_STATE()
en unCATCH
block realmente solo tiene sentido si se cumplen las dos condiciones siguientes: XACT_ABORT
esOFF
(demásXACT_STATE()
siempre debería volver-1
y@@TRANCOUNT
sería todo lo que necesitas)- Tienes lógica en el
CATCH
bloque, o en algún lugar de la cadena si las llamadas están anidadas, eso hace un cambio (unCOMMIT
o incluso cualquier declaración DML, DDL, etc.) en lugar de haciendo unROLLBACK
. (este es un caso de uso muy atípico) ** consulte la nota en la parte inferior, en la sección ACTUALIZACIÓN 3, con respecto a una recomendación no oficial de Microsoft para verificar siempreXACT_STATE()
en lugar de@@TRANCOUNT
y por qué las pruebas muestran que su razonamiento no da resultado. - la introducción de la
TRY...CATCH
La construcción en SQL Server 2005 ha, en su mayor parte, obsoleta laXACT_ABORT ON
propiedad de la sesión, ya que proporciona un mayor grado de control sobre la transacción (al menos tiene la opción deCOMMIT
, siempre queXACT_STATE()
no regresa-1
).
Otra forma de ver esto es, antes de SQL Server 2005,XACT_ABORT ON
proporcionó una forma fácil y confiable de detener el procesamiento cuando se produjo un error, en comparación con la verificación@@ERROR
después de cada declaración. - El código de ejemplo de documentación para
XACT_STATE()
es erróneo, o en el mejor de los casos engañoso, en el sentido de que muestra la comprobación deXACT_STATE() = 1
cuandoXACT_ABORT
esON
.
La parte larga 😉
Sí, ese código de ejemplo en MSDN es un poco confuso (ver también: @@ TRANCOUNT (Rollback) vs.XACT_STATE) ;-). Y creo que es engañoso porque muestra algo que no tiene sentido (por la razón por la que está preguntando: ¿puede incluso tener una transacción “comprometible” en el CATCH
bloquear cuando XACT_ABORT
es ON
), o incluso si es posible, todavía se centra en una posibilidad técnica que pocos querrán o necesitarán alguna vez, e ignora la razón por la que es más probable que uno lo necesite.
Si hay un error lo suficientemente grave dentro del bloque TRY, el control pasará a CATCH. Entonces, si estoy dentro del CATCH, sé que la transacción ha tenido un problema y realmente lo único sensato que se puede hacer en este caso es revertirlo, ¿no es así?
Creo que ayudaría si nos aseguráramos de estar en la misma página con respecto a lo que significan ciertas palabras y conceptos:
-
“error suficientemente grave”: Para ser claros, TRY … CATCH atrapará la mayoría de los errores. La lista de lo que no se detectará se incluye en la página de MSDN vinculada, en la sección “Errores no afectados por una construcción TRY … CATCH”.
-
“si estoy dentro del CATCH, sé que transacción ha tenido un problema “(emphasestá agregado): Si por “transacción” te refieres al lógico unidad de trabajo según lo determinado por usted agrupando declaraciones en una transacción explícita, entonces lo más probable es que sí. Creo que la mayoría de nosotros, la gente de DB, tenderíamos a estar de acuerdo en que la reversión es “la única cosa sensata que se puede hacer”, ya que es probable que tengamos una visión similar de cómo y por qué usamos transacciones explícitas y concebimos qué pasos deben formar una unidad atómica. de trabajo.
Pero, si te refieres al real unidades de trabajo que se agrupan en la transacción explícita, entonces no, no sabe que la transacción en sí ha tenido un problema. Tu solo sabes eso a La instrucción que se ejecuta dentro de la transacción definida explícitamente ha generado un error. Pero puede que no sea una declaración DML o DDL. E incluso si fuera una declaración DML, la Transacción en sí misma podría ser confirmable.
Dados los dos puntos mencionados anteriormente, probablemente deberíamos hacer una distinción entre las transacciones que “no puede” comprometer y las que “no quiere” comprometer.
Cuando XACT_STATE()
devuelve un 1
, eso significa que la transacción es “confirmable”, que puede elegir entre COMMIT
o ROLLBACK
. Puede que no querer para confirmarlo, pero si por alguna razón difícil de dar con un ejemplo por alguna razón deseaba hacerlo, al menos podría hacerlo porque algunas partes de la Transacción se completaron con éxito.
Pero cuando XACT_STATE()
devuelve un -1
, entonces realmente necesitas ROLLBACK
porque una parte de la Transacción entró en mal estado. Ahora, estoy de acuerdo en que si el control se ha pasado al bloque CATCH, entonces tiene suficiente sentido para verificar @@TRANCOUNT
, porque incluso si pudiera realizar la transacción, ¿por qué querría hacerlo?
Pero si observa en la parte superior del ejemplo, la configuración de XACT_ABORT ON
cambia un poco las cosas. Puede tener un error regular, después de hacer BEGIN TRAN
que pasará el control al bloque CATCH cuando XACT_ABORT
es OFF
y XACT_STATE () volverá 1
. PERO, si XACT_ABORT es ON
, entonces la Transacción es “abortada” (es decir, invalidada) por cualquier error y luego XACT_STATE()
volverá -1
. En este caso, parece inútil comprobar XACT_STATE()
dentro de CATCH
bloque, ya que siempre parece devolver un -1
cuando XACT_ABORT
es ON
.
Entonces que es XACT_STATE()
¿por? Algunas pistas son:
-
Página de MSDN para
TRY...CATCH
, en la sección “Transacciones no comprometidas y XACT_STATE”, dice:Un error que normalmente termina una transacción fuera de un bloque TRY hace que una transacción entre en un estado no comprometible cuando el error ocurre dentro de un bloque TRY.
-
La página de MSDN para SET XACT_ABORT, en la sección “Comentarios”, dice:
Cuando SET XACT_ABORT está APAGADO, en algunos En los casos, solo se revierte la instrucción Transact-SQL que generó el error y la transacción continúa procesándose.
y:
XACT_ABORT debe establecerse en ON 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.
-
La página de MSDN para BEGIN TRANSACTION, en la sección “Comentarios”, dice:
La transacción local iniciada por la instrucción BEGIN TRANSACTION se escala a una transacción distribuida si se realizan las siguientes acciones antes de que la instrucción se confirme o se deshaga:
- Se ejecuta una instrucción INSERT, DELETE o UPDATE que hace referencia a una tabla remota en un servidor vinculado. La instrucción INSERT, UPDATE o DELETE falla si el proveedor OLE DB utilizado para acceder al servidor vinculado no admite la interfaz ITransactionJoin.
El uso más aplicable parece estar dentro del contexto de las declaraciones DML del servidor vinculado. Y creo que me encontré con esto hace años. No recuerdo todos los detalles, pero tuvo algo que ver con que el servidor remoto no estaba disponible y, por alguna razón, ese error no quedó atrapado dentro del bloque TRY y nunca se envió al CATCH y así fue. un COMMIT cuando no debería haberlo hecho. Por supuesto que podría ha sido un problema de no tener XACT_ABORT
ajustado a ON
en lugar de fallar en verificar XACT_STATE()
, o posiblemente ambos. Y recuerdo haber leído algo que decía que si usaba servidores vinculados y / o transacciones distribuidas, entonces necesitaba usar XACT_ABORT ON
y / o XACT_STATE()
, pero parece que no puedo encontrar ese documento ahora. Si lo encuentro, lo actualizaré con el enlace.
Aún así, he intentado varias cosas y no puedo encontrar un escenario que tenga XACT_ABORT ON
y pasa el control al CATCH
bloquear con XACT_STATE()
reportando 1
.
Pruebe estos ejemplos para ver el efecto de XACT_ABORT
sobre el valor de XACT_STATE()
:
SET XACT_ABORT OFF;
BEGIN TRY
BEGIN TRAN;
SELECT 1/0 AS [DivideByZero]; -- error, yo!
COMMIT TRAN;
END TRY
BEGIN CATCH
SELECT @@TRANCOUNT AS [@@TRANCOUNT],
XACT_STATE() AS [XactState],
ERROR_MESSAGE() AS [ErrorMessage]
IF (@@TRANCOUNT > 0)
BEGIN
ROLLBACK;
END;
END CATCH;
GO ------------------------------------------------
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN;
SELECT 1/0 AS [DivideByZero]; -- error, yo!
COMMIT TRAN;
END TRY
BEGIN CATCH
SELECT @@TRANCOUNT AS [@@TRANCOUNT],
XACT_STATE() AS [XactState],
ERROR_MESSAGE() AS [ErrorMessage]
IF (@@TRANCOUNT > 0)
BEGIN
ROLLBACK;
END;
END CATCH;
GO ------------------------------------------------
SET XACT_ABORT ON;
BEGIN TRY
SELECT 1/0 AS [DivideByZero]; -- error, yo!
END TRY
BEGIN CATCH
SELECT @@TRANCOUNT AS [@@TRANCOUNT],
XACT_STATE() AS [XactState],
ERROR_MESSAGE() AS [ErrorMessage]
END CATCH;
ACTUALIZAR
Si bien no forma parte de la pregunta original, según estos comentarios sobre esta respuesta:
He estado leyendo los artículos de Erland sobre manejo de errores y transacciones donde dice que
XACT_ABORT
esOFF
de forma predeterminada por motivos heredados y normalmente deberíamos configurarlo enON
.
…
“… si sigue la recomendación y ejecuta con SET XACT_ABORT ON, la transacción siempre estará condenada”.
Antes de usar XACT_ABORT ON
en todas partes, me preguntaría: ¿qué se gana exactamente aquí? No lo he considerado necesario y, en general, defiendo que se debe usar solo cuando sea necesario. Si quieres o no ROLLBACK
se puede manejar con bastante facilidad usando la plantilla que se muestra en la respuesta de @ Remus, o la que he estado usando durante años que es esencialmente lo mismo pero sin el punto de guardado, como se muestra en esta respuesta (que maneja llamadas anidadas):
¿Estamos obligados a manejar la transacción en el código C # así como en el procedimiento almacenado?
ACTUALIZACIÓN 2
Hice un poco más de pruebas, esta vez creando una pequeña aplicación de consola .NET, creando una transacción en la capa de la aplicación, antes de ejecutar cualquier SqlCommand
objetos (es decir, a través de using (SqlTransaction _Tran = _Connection.BeginTransaction()) { ...
), además de utilizar un error de cancelación de lotes en lugar de solo un error de cancelación de declaraciones, y descubrió que:
- Una transacción “no compatible” es aquella que, en su mayor parte, ya se ha revertido (los cambios se han deshecho), pero
@@TRANCOUNT
sigue siendo> 0. - Cuando tiene una Transacción “no compatible”, no puede emitir una
COMMIT
ya que eso generará un error diciendo que la Transacción es “no comprometible”. Tampoco puede ignorarlo / no hacer nada, ya que se generará un error cuando el lote termine indicando que el lote se completó con una transacción persistente no comprometida y se revertirá (entonces, um, si se revertirá automáticamente de todos modos, ¿Por qué molestarse en lanzar el error?). Vos tambien debe emitir un explícitoROLLBACK
, tal vez no en el inmediatoCATCH
bloque, pero antes de que termine el lote. - en un
TRY...CATCH
construir, cuandoXACT_ABORT
esOFF
, errores que terminarían la transacción automáticamente si hubieran ocurrido fuera de unTRY
bloque, como errores de aborto de lotes, deshará el trabajo pero no terminará la transacción, dejándola como “no compatible”. Emitir unROLLBACK
Es más una formalidad necesaria para cerrar la Transacción, pero el trabajo ya se ha revertido. - Cuando
XACT_ABORT
esON
, la mayoría de los errores actúan como abortos por lotes y, por lo tanto, se comportan como se describe en la viñeta directamente arriba (# 3). XACT_STATE()
, al menos en unCATCH
bloque, mostrará un-1
para errores de aborto de lotes si había una transacción activa en el momento del error.XACT_STATE()
a veces regresa1
incluso cuando no hay ninguna transacción activa. Si@@SPID
(entre otros) está en elSELECT
lista junto conXACT_STATE()
, luegoXACT_STATE()
devolverá 1 cuando no haya ninguna transacción activa. Este comportamiento comenzó en SQL Server 2012 y existe en 2014, pero no lo he probado en 2016.
Teniendo en cuenta los puntos anteriores:
- Dados los puntos n. ° 4 y n. ° 5, dado que la mayoría (¿o todos?) Los errores harán que una transacción sea “incompatible”, parece totalmente inútil verificar
XACT_STATE()
en elCATCH
bloquear cuandoXACT_ABORT
esON
ya que el valor devuelto siempre será-1
. - Comprobación
XACT_STATE()
en elCATCH
bloquear cuandoXACT_ABORT
esOFF
tiene más sentido porque el valor de retorno tendrá al menos alguna variación, ya que devolverá1
para errores de aborto de declaraciones. Sin embargo, si codifica como la mayoría de nosotros, esta distinción no tiene sentido ya que llamaráROLLBACK
de todos modos simplemente por el hecho de que se produjo un error. - Si encuentra una situación que justifique la emisión de una
COMMIT
en elCATCH
cuadra, luego comprobar el valor deXACT_STATE()
y asegúrate deSET XACT_ABORT OFF;
. XACT_ABORT ON
parece ofrecer poco o ningún beneficio sobre elTRY...CATCH
construir.- No puedo encontrar ningún escenario donde comprobar
XACT_STATE()
proporciona un beneficio significativo sobre simplemente verificar@@TRANCOUNT
. - Tampoco puedo encontrar ningún escenario donde
XACT_STATE()
devoluciones1
en unCATCH
bloquear cuandoXACT_ABORT
esON
. Creo que es un error de documentación. - Sí, puede revertir una transacción que no comenzó explícitamente. Y en el contexto de usar
XACT_ABORT ON
, es un punto discutible ya que ocurre un error en unTRY
block revertirá automáticamente los cambios. - los
TRY...CATCH
constructo tiene el beneficio sobreXACT_ABORT ON
en no cancelar automáticamente toda la Transacción y, por lo tanto, permitir la Transacción (siempre queXACT_STATE()
devoluciones1
) para ser comprometido (incluso si este es un caso de borde).
Ejemplo de XACT_STATE()
regresando -1
cuando XACT_ABORT
es OFF
:
SET XACT_ABORT OFF;
BEGIN TRY
BEGIN TRAN;
SELECT CONVERT(INT, 'g') AS [ConversionError];
COMMIT TRAN;
END TRY
BEGIN CATCH
DECLARE @State INT;
SET @State = XACT_STATE();
SELECT @@TRANCOUNT AS [@@TRANCOUNT],
@State AS [XactState],
ERROR_MESSAGE() AS [ErrorMessage];
IF (@@TRANCOUNT > 0)
BEGIN
SELECT 'Rollin back...' AS [Transaction];
ROLLBACK;
END;
END CATCH;
ACTUALIZACIÓN 3
Relacionado con el elemento n. ° 6 en la sección ACTUALIZACIÓN 2 (es decir, posible valor incorrecto devuelto por XACT_STATE()
cuando no hay ninguna transacción activa):
-
El comportamiento extraño / erróneo comenzó en SQL Server 2012 (hasta ahora probado contra 2012 SP2 y 2014 SP1)
-
En las versiones 2005, 2008 y 2008 R2 de SQL Server,
XACT_STATE()
no informó los valores esperados cuando se utilizó en Triggers oINSERT...EXEC
escenarios: xact_state () no se puede usar de manera confiable para determinar si una transacción está condenada (página archivada). Sin embargo, en estas 3 versiones (solo probé en 2008 R2),XACT_STATE()
lo hace no informar incorrectamente1
cuando se usa en unSELECT
con@@SPID
. -
Allí
esera un error de conexión presentado contra el comportamiento mencionado aquí, pero se cierra como “Por diseño”: XACT_STATE () puede devolver un estado de transacción incorrecto en SQL 2012(el enlace ya no es válido debido a la migración del sitio por incompetencia y / o negligencia grave). Sin embargo, la prueba se realizó al seleccionar de un DMV y se concluyó que al hacerlo, naturalmente, el sistema generaría una transacción, al menos para algunos DMV. También se afirmó en la respuesta final de MS que:Tenga en cuenta que una instrucción IF, y también un SELECT sin FROM, no inician una transacción.
por ejemplo, ejecutar SELECT XACT_STATE () si no tiene una transacción previamente existente devolverá 0.Esas declaraciones son incorrectas dado el siguiente ejemplo:
SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @@SPID AS [SPID]; GO DECLARE @SPID INT; SET @SPID = @@SPID; SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @SPID AS [SPID]; GO
-
Por lo tanto, presenté un nuevo error de retroalimentación:
XACT_STATE () devuelve 1 cuando se usa en SELECT con algunas variables del sistema pero sin la cláusula FROM
TENGA EN CUENTA que en el elemento “XACT_STATE () puede devolver un estado de transacción incorrecto en SQL 2012” Connect vinculado directamente arriba, Microsoft (bueno, un representante de) afirma:
@@ trancount devuelve el número de declaraciones BEGIN TRAN. Por tanto, no es un indicador fiable de si hay una transacción activa. XACT_STATE () también devuelve 1 si hay una transacción activa de confirmación automática y, por lo tanto, es un indicador más confiable de si hay una transacción activa.
Sin embargo, no puedo encontrar ninguna razón para no confiar @@TRANCOUNT
. La siguiente prueba muestra que @@TRANCOUNT
de hecho regresa 1
en una transacción de confirmación automática:
--- begin setup
GO
CREATE PROCEDURE #TransactionInfo AS
SET NOCOUNT ON;
SELECT @@TRANCOUNT AS [TranCount],
XACT_STATE() AS [XactState];
GO
--- end setup
DECLARE @Test TABLE (TranCount INT, XactState INT);
SELECT * FROM @Test; -- no rows
EXEC #TransactionInfo; -- 0 for both fields
INSERT INTO @Test (TranCount, XactState)
EXEC #TransactionInfo;
SELECT * FROM @Test; -- 1 row; 1 for both fields
También probé en una mesa real con un Trigger y @@TRANCOUNT
dentro del Trigger informó con precisión 1
aunque no se haya iniciado ninguna transacción explícita.
Yo abordaría esto de manera diferente. XACT_ABORT_ON
es un mazo, puede utilizar un enfoque más refinado, consulte 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
Este enfoque revertirá, cuando sea posible, solo el trabajo realizado dentro del bloque TRY y restaurará el estado al estado anterior a entrar en el bloque TRY. De esta manera, puede realizar un procesamiento complejo, como iterar un cursor, sin perder todo el trabajo en caso de error. El único inconveniente es que, al utilizar los puntos de salvamento de transacciones, no puede utilizar nada que sea incompatible con los puntos de salvamento, como las transacciones distribuidas.
Reseñas y puntuaciones del artículo
Si te apasiona este mundo, tienes el poder dejar una noticia acerca de qué le añadirías a este tutorial.