Saltar al contenido

¿En qué casos se puede confirmar una transacción desde el interior del bloque CATCH cuando XACT_ABORT se establece en ON?

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 con XACT_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 establezco XACT_ABORT para OFF, 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 cuando XACT_ABORT se establece en ON.

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 > 0y 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 cuando XACT_ABORT se establece en OFF

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:

  1. 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 el XACT_STATE() función para obtener esa información.
  2. comprobación XACT_STATE() en un CATCH block realmente solo tiene sentido si se cumplen las dos condiciones siguientes:
  3. XACT_ABORT es OFF (demás XACT_STATE() siempre debería volver -1 y @@TRANCOUNT sería todo lo que necesitas)
  4. 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 (un COMMIT o incluso cualquier declaración DML, DDL, etc.) en lugar de haciendo un ROLLBACK. (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 siempre XACT_STATE() en lugar de @@TRANCOUNTy por qué las pruebas muestran que su razonamiento no da resultado.
  5. la introducción de la TRY...CATCH La construcción en SQL Server 2005 ha, en su mayor parte, obsoleta la XACT_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 de COMMIT, siempre que XACT_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.
  6. 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 de XACT_STATE() = 1 cuando XACT_ABORT es ON.

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 es OFF de forma predeterminada por motivos heredados y normalmente deberíamos configurarlo en ON.

“… 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:

  1. 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.
  2. 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ícito ROLLBACK, tal vez no en el inmediato CATCH bloque, pero antes de que termine el lote.
  3. en un TRY...CATCH construir, cuando XACT_ABORT es OFF, errores que terminarían la transacción automáticamente si hubieran ocurrido fuera de un TRY bloque, como errores de aborto de lotes, deshará el trabajo pero no terminará la transacción, dejándola como “no compatible”. Emitir un ROLLBACK Es más una formalidad necesaria para cerrar la Transacción, pero el trabajo ya se ha revertido.
  4. Cuando XACT_ABORT es ON, 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).
  5. XACT_STATE(), al menos en un CATCH bloque, mostrará un -1 para errores de aborto de lotes si había una transacción activa en el momento del error.
  6. XACT_STATE() a veces regresa 1 incluso cuando no hay ninguna transacción activa. Si @@SPID (entre otros) está en el SELECT lista junto con XACT_STATE(), luego XACT_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 el CATCH bloquear cuando XACT_ABORT es ON ya que el valor devuelto siempre será -1.
  • Comprobación XACT_STATE() en el CATCH bloquear cuando XACT_ABORT es OFF 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 el CATCH cuadra, luego comprobar el valor de XACT_STATE()y asegúrate de SET XACT_ABORT OFF;.
  • XACT_ABORT ON parece ofrecer poco o ningún beneficio sobre el TRY...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() devoluciones 1 en un CATCH bloquear cuando XACT_ABORT es ON. 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 un TRY block revertirá automáticamente los cambios.
  • los TRY...CATCH constructo tiene el beneficio sobre XACT_ABORT ON en no cancelar automáticamente toda la Transacción y, por lo tanto, permitir la Transacción (siempre que XACT_STATE() devoluciones 1) 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 o INSERT...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 incorrectamente 1 cuando se usa en un SELECT 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.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *