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.

Vignesh Kumar A
fuente
¿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
Remus Rusanu
fuente
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.

seguso
fuente
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

Amarnath Balasubramanian
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í .

Niklasolsn
fuente
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 ...

Justin
fuente
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
Casey Crookston
fuente
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
punto hexadecimal
fuente
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

Zsombor Zsuffa
fuente
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]
Ken Palmer
fuente
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!

omostan
fuente
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

Sen Alexandru
fuente
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

Rajan Tikare
fuente
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

Nitin Patwekari
fuente
0

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

SELECT 151
RETURN -151

Entonces usa:

SELECT 151
ROLLBACK
RETURN -151
Vidyesh
fuente