El recuento de transacciones después de EXECUTE indica un número no coincidente de instrucciones BEGIN y COMMIT. Recuento anterior = 1, recuento actual = 0


95

Tengo un Insertprocedimiento almacenado que alimentará datos Table1y obtendrá el Column1valor Table1y llamará al segundo procedimiento almacenado que alimentará el Table2.

Pero cuando llamo al segundo procedimiento almacenado como:

Exec USPStoredProcName

Obtuve el siguiente error:

El recuento de transacciones después de EXECUTE indica un número no coincidente de instrucciones BEGIN y COMMIT. Recuento anterior = 1, recuento actual = 0.

He leído las respuestas en otras preguntas similares y no puedo encontrar dónde se está estropeando exactamente el recuento de confirmaciones.


¿Tiene algún bloque TRY / CATCH en su procedimiento?
Remus Rusanu

Sí, tengo bloque TRY / CATCH
Vignesh Kumar A

Respuestas:


110

Si tiene un bloque TRY / CATCH, la causa probable es que esté detectando una excepción de cancelación de transacción y continúe. En el bloque CATCH siempre debe marcar XACT_STATE()y manejar las transacciones apropiadas abortadas y no compatibles (condenadas). Si la persona que llama inicia una transacción y la llamada llega, digamos, a un punto muerto (que canceló la transacción), ¿cómo va a comunicar la persona que llama a la persona que llama que la transacción fue cancelada y que no debería continuar con el 'negocio como de costumbre'? La única forma viable es volver a generar una excepción, lo que obliga a la persona que llama a manejar la situación. Si traga silenciosamente una transacción abortada y la persona que llama continúa asumiendo que todavía está en la transacción original, solo el caos puede asegurarse (y el error que obtiene es la forma en que el motor intenta protegerse).

Le recomiendo que revise el manejo de excepciones y las transacciones anidadas, que muestra un patrón que se puede usar con transacciones anidadas y excepciones:

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

3
Gracias por tu ayuda. Al usar Raiserror, encontré el problema. Se trata de intentar insertar un valor NULL en el campo NOT NULL
Vignesh Kumar A

Pero una validación de verificación de restricción no abortaría la transacción. ¿Está retrocediendo explícitamente en la captura o lo usa xact_abort on?
Remus Rusanu

Estoy retrocediendo explícitamente
Vignesh Kumar A

2
Probé este patrón pero aún no funciona; cuando tengo una transacción externa, este patrón crea un punto de guardado y, en caso de un error crítico (transacción no compatible), revierte la transacción externa, esto todavía causa un @@ trancount = 1 antes de ingresar procedimiento y @@ trancount = 0 al salir
gorrión

3
Creo que este bit en la captura es incorrecta: if @xstate = -1 rollback; En cuanto a este ejemplo de MSDN , debemos no retrotraer la transacción completa a menos que hubiera no una transacción externa (es decir, a menos que hicimos begin tran). Creo que el procedimiento debería ser solo rollbacksi comenzamos la transacción, lo que solucionaría el problema de @ sparrow.
Nick

60

Yo tuve este problema también. Para mí, la razón era que estaba haciendo

return
commit

en vez de

commit
return   

en un procedimiento almacenado.


4
@seguso - esto fue muy útil. Gracias por compartir. A veces algo tan simplemente se mete debajo del polvo. Les pasa a los mejores.
Leo Gurdian

Este fue el problema para mí, pero fue menos obvio porque estábamos agrupando varias llamadas sproc en una gran transacción a través de nuestra capa de acceso a datos, por lo que con solo mirar el sproc no se podía decir que había una transacción en absoluto. Si tiene este problema, asegúrese de que no haya nada fuera del propio sproc que esté creando una transacción. Si lo hay, es posible que no pueda usar declaraciones de retorno dentro del sproc en absoluto.
EF0

Este era yo, tenía una transacción y regresaba antes de mi transacción de confirmación en una declaración if / else
Kevin

18

Esto sucede normalmente cuando se inicia la transacción y no se confirma o no se deshace.

En caso de que el error aparezca en su procedimiento almacenado, esto puede bloquear las tablas de la base de datos porque la transacción no se completa debido a algunos errores de tiempo de ejecución en ausencia de manejo de excepciones. Puede usar el manejo de excepciones como se muestra a continuación. SET XACT_ABORT

SET XACT_ABORT ON
SET NoCount ON
Begin Try 
     BEGIN TRANSACTION 
        //Insert ,update queries    
     COMMIT
End Try 
Begin Catch 
     ROLLBACK
End Catch

Fuente


Si este fuera el caso, la pregunta / respuesta citada probablemente debería significar que esta debería marcarse como duplicada y cerrada
Mark Schultheiss

10

Tenga en cuenta que si utiliza transacciones anidadas, una operación ROLLBACK revierte todas las transacciones anidadas, incluida la más externa.

Esto podría, con el uso en combinación con TRY / CATCH, dar como resultado el error que describió. Vea más aquí .


5

Esto también puede ocurrir si su procedimiento almacenado encuentra un error de compilación después de abrir una transacción (por ejemplo, tabla no encontrada, nombre de columna no válido).

Descubrí que tenía que usar 2 procedimientos almacenados, uno de "trabajador" y uno de envoltura con try / catch, ambos con una lógica similar a la descrita por Remus Rusanu. La captura de trabajador se usa para manejar los errores "normales" y la captura de contenedor para manejar los errores de falla de compilación.

https://msdn.microsoft.com/en-us/library/ms175976.aspx

Errores que no se ven afectados por una construcción TRY… CATCH

Los siguientes tipos de errores no son manejados por un bloque CATCH cuando ocurren en el mismo nivel de ejecución que la construcción TRY… CATCH:

  • Compile errores, como errores de sintaxis , que impiden la ejecución de un lote.
  • Errores que ocurren durante la recompilación a nivel de instrucción, como errores de resolución de nombres de objetos que ocurren después de la compilación debido a una resolución de nombres diferida.

Esperemos que esto ayude a alguien más a ahorrar unas horas de depuración ...


1
Gracias Justin. Buena observación. En mi caso, estaba haciendo un agregado dentro de una actualización que no produce errores de compilación durante el guardado del SP, pero de hecho era una sintaxis inválida - "Un agregado puede no aparecer en la lista de conjuntos de una declaración UPDATE"
kuklei

4

En mi caso, el error fue causado por un RETURNarchivo BEGIN TRANSACTION. Entonces tuve algo como esto:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Return
 End
commit

y debe ser:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Rollback Transaction ----- THIS WAS MISSING
     Return
 End
commit

2

Para mí, después de una depuración extensa, la solución fue un simple tiro faltante; declaración en la captura después de la reversión. Sin él, este feo mensaje de error es con lo que terminas.

begin catch
    if @@trancount > 0 rollback transaction;
    throw; --allows capture of useful info when an exception happens within the transaction
end catch

2

Tuve el mismo mensaje de error, mi error fue que tenía un punto y coma al final de la línea COMMIT TRANSACTION


Tan simple como esto. Además, mi caso necesitaba una declaración 'ROLLBACK' en caso de que el SP no se ejecutara por completo. Solo para cerrar / finalizar la transacción.
J Cordero

1

Encontré este error una vez después de omitir esta declaración de mi transacción.

COMMIT TRANSACTION [MyTransactionName]

1

En mi opinión, la respuesta aceptada es en la mayoría de los casos una exageración.

La causa del error a menudo es la falta de coincidencia de BEGIN y COMMIT como se indica claramente en el error. Esto significa usar:

Begin
  Begin
    -- your query here
  End
commit

en vez de

Begin Transaction
  Begin
    -- your query here
  End
commit

¡omitir Transacción después de Comenzar causa este error!


1

Asegúrese de no tener varias transacciones en el mismo procedimiento / consulta, de las cuales una o más quedan sin comprometer.

En mi caso, accidentalmente tuve una declaración BEGIN TRAN en la consulta


1

Esto también puede depender de la forma en que invoca el SP desde su código C #. Si el SP devuelve algún valor de tipo de tabla, invoque el SP con ExecuteStoreQuery, y si el SP no devuelve ningún valor, invoque el SP con ExecuteStoreCommand


1

Evitar el uso de

RETURN

declaración cuando estás usando

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

y

BEGIN, COMMIT & ROLLBACK

declaraciones en procedimientos almacenados SQL


0

Si tiene una estructura de código de algo como:

SELECT 151
RETURN -151

Entonces usa:

SELECT 151
ROLLBACK
RETURN -151
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.