Il conteggio delle transazioni dopo EXECUTE indica un numero non corrispondente di istruzioni BEGIN e COMMIT. Conteggio precedente = 1, conteggio corrente = 0


95

Ho una Insertprocedura memorizzata che alimenterà i dati Table1e otterrà il Column1valore da Table1e chiamerà la seconda procedura memorizzata che alimenterà il Table2.

Ma quando chiamo La seconda procedura memorizzata come:

Exec USPStoredProcName

Ottengo il seguente errore:

Il conteggio delle transazioni dopo EXECUTE indica un numero non corrispondente di istruzioni BEGIN e COMMIT. Conteggio precedente = 1, conteggio corrente = 0.

Ho letto le risposte in altre domande simili e non sono in grado di trovare dove esattamente il conteggio dei commit viene incasinato.


Hai dei blocchi TRY / CATCH nella tua procedura?
Remus Rusanu

Sì, ho il blocco TRY / CATCH
Vignesh Kumar A

Risposte:


109

Se hai un blocco TRY / CATCH, la causa probabile è che stai rilevando un'eccezione di interruzione della transazione e continui. Nel blocco CATCH devi sempre controllare XACT_STATE()e gestire le transazioni abortite e non modificabili (condannate) appropriate. Se il chiamante avvia una transazione e il calee colpisce, ad esempio, un deadlock (che ha interrotto la transazione), come farà il chiamato a comunicare al chiamante che la transazione è stata interrotta e non dovrebbe continuare con "business as usual"? L'unico modo possibile è rilanciare un'eccezione, costringendo il chiamante a gestire la situazione. Se ingoi silenziosamente una transazione interrotta e il chiamante continua a supporre che sia ancora nella transazione originale, solo il caos può garantire (e l'errore che ottieni è il modo in cui il motore cerca di proteggersi).

Ti consiglio di esaminare la gestione delle eccezioni e le transazioni nidificate che mostra un modello che può essere utilizzato con transazioni ed eccezioni nidificate:

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
Grazie per l'aiuto. Utilizzando Raiserror ho trovato il problema: si tratta di provare a inserire un valore NULL nel campo NOT NULL
Vignesh Kumar A

Ma una convalida del controllo dei vincoli non interromperà la transazione. Stai esplicitamente tornando indietro nella cattura o usi xact_abort on?
Remus Rusanu

Sto tornando indietro esplicitamente
Vignesh Kumar A

2
Ho provato questo modello ma ancora non funziona - quando ho una transazione esterna questo modello crea un punto di salvataggio e in caso di errore critico (transazione non bloccabile) esegue il rollback della transazione esterna - questo causa ancora un @@ trancount = 1 prima di entrare procedura e @@ trancount = 0 all'uscita
passero

3
Penso che questo bit nella cattura è sbagliato: if @xstate = -1 rollback; Guardando a questo esempio MSDN , dovremmo non rollback della transazione completa a meno che non ci fosse non una transazione esterna (che è, a meno che non abbiamo fatto begin tran). Penso che la procedura dovrebbe solo rollbackse avessimo avviato la transazione, il che risolverebbe il problema di @ sparrow.
Nick

60

Anch'io ho avuto questo problema. Per me, il motivo era che stavo facendo

return
commit

invece di

commit
return   

in una procedura memorizzata.


4
@seguso - è stato molto utile. Grazie per aver condiviso. A volte qualcosa finisce così semplicemente sotto la polvere. Succede ai migliori di loro.
Leo Gurdian

Questo era il problema per me, ma era meno ovvio perché stavamo avvolgendo diverse chiamate sproc in una grande transazione tramite il nostro livello di accesso ai dati, quindi solo guardando lo sproc non potevi dire che c'era una transazione. Se hai questo problema, assicurati che non ci sia qualcosa al di fuori dello sproc stesso che sta creando una transazione. In tal caso potresti non essere in grado di utilizzare le istruzioni return all'interno di sproc.
EF0

Questo ero io, avevo una transazione e stavo tornando prima della mia transazione di commit in un'istruzione if / else
Kevin

18

Ciò accade normalmente quando la transazione viene avviata e non viene eseguito il commit o non viene eseguito il rollback.

Nel caso in cui l'errore si verifichi nella procedura memorizzata, questo può bloccare le tabelle del database perché la transazione non è stata completata a causa di alcuni errori di runtime in assenza di gestione delle eccezioni È possibile utilizzare la gestione delle eccezioni come di seguito. 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

fonte


Se questo fosse il caso, la domanda / risposta citata dovrebbe probabilmente significare che questa dovrebbe essere contrassegnata come duplicata e chiusa
Mark Schultheiss

10

Tenere presente che se si utilizzano transazioni nidificate, un'operazione ROLLBACK ripristina tutte le transazioni nidificate, inclusa quella più esterna.

Questo potrebbe, con l'utilizzo in combinazione con TRY / CATCH, causare l'errore descritto. Vedi di più qui .


5

Ciò può verificarsi anche se la procedura memorizzata incontra un errore di compilazione dopo l'apertura di una transazione (ad es. Tabella non trovata, nome di colonna non valido).

Ho scoperto di dover utilizzare 2 stored procedure una "worker" e una wrapper con try / catch entrambe con logica simile a quella delineata da Remus Rusanu. Il catch worker viene utilizzato per gestire gli errori "normali" e il catch wrapper per gestire gli errori di errore di compilazione.

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

Errori non influenzati da un costrutto TRY… CATCH

I seguenti tipi di errori non vengono gestiti da un blocco CATCH quando si verificano allo stesso livello di esecuzione del costrutto TRY… CATCH:

  • Errori di compilazione, come errori di sintassi , che impediscono l'esecuzione di un batch.
  • Errori che si verificano durante la ricompilazione a livello di istruzione, ad esempio errori di risoluzione dei nomi degli oggetti che si verificano dopo la compilazione a causa della risoluzione dei nomi differita.

Si spera che questo aiuti qualcun altro a risparmiare qualche ora di debug ...


1
Grazie Justin. Bella osservazione. Nel mio caso stavo facendo un aggregato all'interno di un aggiornamento che non produce errori di compilazione durante il salvataggio di SP ma era effettivamente una sintassi non valida - "Un aggregato potrebbe non apparire nell'elenco di set di un'istruzione UPDATE"
kuklei

4

Nel mio caso, l'errore è stato causato da un RETURNall'interno del BEGIN TRANSACTION. Quindi ho avuto qualcosa del genere:

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

e deve essere:

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

2

Per me, dopo un lungo debug, la correzione era un semplice lancio mancante; dichiarazione nella cattura dopo il rollback. Senza di esso questo brutto messaggio di errore è ciò che ti ritroverai con.

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

2

Ho ricevuto lo stesso messaggio di errore, il mio errore era che avevo un punto e virgola alla fine della riga COMMIT TRANSACTION


Semplice come questo. Inoltre, il mio caso necessitava di un'istruzione "ROLLBACK" nel caso in cui l'SP non fosse completamente eseguito. Solo per chiudere / terminare la transazione.
J Cordero

1

Ho riscontrato questo errore una volta dopo aver omesso questa istruzione dalla mia transazione.

COMMIT TRANSACTION [MyTransactionName]

1

A mio parere, la risposta accettata è nella maggior parte dei casi eccessiva.

La causa dell'errore è spesso la mancata corrispondenza tra BEGIN e COMMIT, come chiaramente indicato dall'errore. Ciò significa utilizzare:

Begin
  Begin
    -- your query here
  End
commit

invece di

Begin Transaction
  Begin
    -- your query here
  End
commit

omettere Transaction dopo Begin causa questo errore!


1

Assicurati di non avere più transazioni nella stessa procedura / query di cui una o più vengono lasciate non vincolate.

Nel mio caso, ho ricevuto accidentalmente un'istruzione BEGIN TRAN nella query


1

Ciò può dipendere anche dal modo in cui si richiama l'SP dal codice C #. Se l'SP restituisce un valore di tipo tabella, richiamare l'SP con ExecuteStoreQuery e, se l'SP non restituisce alcun valore, richiamare l'SP con ExecuteStoreCommand


1

Evitare di utilizzare

RETURN

dichiarazione quando si utilizza

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

e

BEGIN, COMMIT & ROLLBACK

istruzioni nelle procedure memorizzate SQL


0

Se hai una struttura del codice simile a:

SELECT 151
RETURN -151

Quindi usa:

SELECT 151
ROLLBACK
RETURN -151
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.