Die Transaktionsanzahl nach EXECUTE zeigt eine nicht übereinstimmende Anzahl von BEGIN- und COMMIT-Anweisungen an. Vorherige Zählung = 1, aktuelle Zählung = 0


91

Ich habe eine Insertgespeicherte Prozedur, die Daten in die zweite gespeicherte Prozedur einspeist Table1und den Column1Wert von Table1dieser abruft und die zweite gespeicherte Prozedur aufruft, die die Tabelle 2 speist.

Aber wenn ich die zweite gespeicherte Prozedur aufrufe als:

Exec USPStoredProcName

Ich erhalte folgende Fehlermeldung:

Die Transaktionsanzahl nach EXECUTE zeigt eine nicht übereinstimmende Anzahl von BEGIN- und COMMIT-Anweisungen an. Vorherige Zählung = 1, aktuelle Zählung = 0.

Ich habe die Antworten in anderen solchen Fragen gelesen und kann nicht feststellen, wo genau die Anzahl der Festschreibungen durcheinander gebracht wird.


Haben Sie TRY / CATCH-Blöcke in Ihrer Prozedur?
Remus Rusanu

Ja, ich habe TRY / CATCH-Block
Vignesh Kumar A

Antworten:


107

Wenn Sie einen TRY / CATCH-Block haben, ist die wahrscheinliche Ursache, dass Sie eine Transaktionsabbruchausnahme abfangen und fortfahren. Im CATCH-Block müssen Sie immer die XACT_STATE()entsprechenden abgebrochenen und nicht festgeschriebenen (zum Scheitern verurteilten) Transaktionen überprüfen und verarbeiten. Wenn Ihr Anrufer eine Transaktion startet und der Calee beispielsweise auf einen Deadlock stößt (der die Transaktion abgebrochen hat), wie wird der Angerufene dem Anrufer mitteilen, dass die Transaktion abgebrochen wurde und nicht mit "Business as usual" fortfahren sollte? Der einzig mögliche Weg besteht darin, eine Ausnahme erneut auszulösen und den Anrufer zu zwingen, mit der Situation umzugehen. Wenn Sie eine abgebrochene Transaktion stillschweigend verschlucken und der Anrufer weiterhin davon ausgeht, dass sie sich noch in der ursprünglichen Transaktion befindet, kann nur Chaos sicherstellen (und der Fehler, den Sie erhalten, ist die Art und Weise, wie die Engine versucht, sich selbst zu schützen).

Ich empfehle Ihnen, die Ausnahmebehandlung und verschachtelte Transaktionen zu überprüfen, die ein Muster anzeigt, das mit verschachtelten Transaktionen und Ausnahmen verwendet werden kann:

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
Danke für Ihre Hilfe. Durch die Verwendung von Raiserror habe ich das Problem gefunden. Es geht darum, einen NULL-Wert in das NOT NULL-Feld einzufügen
Vignesh Kumar A

Eine Überprüfung der Einschränkungsprüfung würde die Transaktion jedoch nicht abbrechen. Rollen Sie explizit in den Fang zurück oder verwenden Sie xact_abort on?
Remus Rusanu


2
Ich habe dieses Muster ausprobiert, aber es funktioniert immer noch nicht. Wenn ich eine externe Transaktion habe, erstellt dieses Muster einen Sicherungspunkt und im Falle eines kritischen Fehlers (nicht zustimmbare Transaktion) wird die äußere Transaktion zurückgesetzt. Dies führt vor der Eingabe immer noch zu einem @@ trancount = 1 Prozedur und @@ trancount = 0 beim Verlassen
Spatz

3
Ich denke , das Bit in der CATCH falsch ist: if @xstate = -1 rollback; Bei diesem Blick MSDN Beispiel , sollten wir nicht die vollständige Transaktion Rollback , es sei denn es gab nicht eine äußere Transaktion (das heißt, es sei denn , wir haben begin tran). Ich denke, das Verfahren sollte nur durchgeführt werden, rollbackwenn wir die Transaktion gestartet haben, wodurch das Problem von @ sparrow behoben würde.
Nick

59

Ich hatte auch dieses Problem. Für mich war der Grund, dass ich es tat

return
commit

anstatt

commit
return   

in einer gespeicherten Prozedur.


4
@seguso - das war sehr hilfreich. Ich danke Ihnen für das Teilen. Manchmal kommt etwas so einfach unter den Staub. Passiert den Besten.
Leo Gurdian

Dies war das Problem für mich, aber es war weniger offensichtlich, da wir über unsere Datenzugriffsschicht mehrere Sproc-Anrufe in eine große Transaktion verpackten. Wenn Sie also nur den Sproc betrachten, können Sie nicht erkennen, dass überhaupt eine Transaktion vorliegt. Wenn Sie dieses Problem haben, stellen Sie sicher, dass sich außerhalb des Sproc selbst nichts befindet, das eine Transaktion erstellt. Wenn dies der Fall ist, können Sie möglicherweise überhaupt keine return-Anweisungen innerhalb des sproc verwenden.
EF0

Dies war ich, ich hatte eine Transaktion und kehrte vor meiner Commit-Transaktion in einer if / else-Anweisung zurück
Kevin

18

Dies geschieht normalerweise, wenn die Transaktion gestartet wird und entweder nicht festgeschrieben oder nicht zurückgesetzt wird.

Falls der Fehler in Ihrer gespeicherten Prozedur auftritt, kann dies die Datenbanktabellen sperren, da die Transaktion aufgrund einiger Laufzeitfehler ohne Ausnahmebehandlung nicht abgeschlossen wird. Sie können die Ausnahmebehandlung wie unten verwenden. 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

Quelle


Wenn dies der Fall wäre, sollte die zitierte Frage / Antwort wahrscheinlich bedeuten, dass diese als doppelt markiert und geschlossen sein sollte
Mark Schultheiss

10

Beachten Sie, dass bei Verwendung verschachtelter Transaktionen bei einer ROLLBACK-Operation alle verschachtelten Transaktionen zurückgesetzt werden, einschließlich der äußersten.

Dies kann bei Verwendung in Kombination mit TRY / CATCH zu dem von Ihnen beschriebenen Fehler führen. Sehen Sie hier mehr .


5

Dies kann auch auftreten, wenn Ihre gespeicherte Prozedur nach dem Öffnen einer Transaktion auf einen Kompilierungsfehler stößt (z. B. Tabelle nicht gefunden, ungültiger Spaltenname).

Ich stellte fest, dass ich 2 gespeicherte Prozeduren verwenden musste, eine "Worker" -Prozedur und eine Wrapper-Prozedur mit Try / Catch, beide mit einer Logik ähnlich der von Remus Rusanu beschriebenen. Der Worker-Catch wird verwendet, um die "normalen" Fehler zu behandeln, und der Wrapper-Catch, um Kompilierungsfehler zu behandeln.

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

Fehler, die von einem TRY… CATCH-Konstrukt nicht betroffen sind

Die folgenden Fehlertypen werden von einem CATCH-Block nicht behandelt, wenn sie auf derselben Ausführungsebene wie das Konstrukt TRY… CATCH auftreten:

  • Kompilierungsfehler, z. B. Syntaxfehler , die die Ausführung eines Stapels verhindern.
  • Fehler, die während der Neukompilierung auf Anweisungsebene auftreten, z. B. Fehler bei der Auflösung von Objektnamen, die nach der Kompilierung aufgrund der verzögerten Namensauflösung auftreten.

Hoffentlich hilft dies jemand anderem, ein paar Stunden Debugging zu sparen ...


1
Danke Justin. Schöne Beobachtung. In meinem Fall habe ich ein Aggregat innerhalb eines Updates erstellt, das beim SP-Speichern keine Kompilierungsfehler erzeugt, aber tatsächlich eine ungültige Syntax aufweist - "Ein Aggregat wird möglicherweise nicht in der Set-Liste einer UPDATE-Anweisung
angezeigt

4

In meinem Fall wurde der Fehler durch ein RETURNinnerhalb des BEGIN TRANSACTION. Also hatte ich so etwas:

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

und es muss sein:

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

2

Für mich war das Update nach ausgiebigem Debuggen ein einfacher fehlender Wurf; Anweisung im Fang nach dem Rollback. Ohne sie ist diese hässliche Fehlermeldung das, was Sie am Ende haben.

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

2

Ich hatte die gleiche Fehlermeldung, mein Fehler war, dass ich am Ende der Zeile COMMIT TRANSACTION ein Semikolon hatte


So einfach ist das. Außerdem benötigte mein Fall eine 'ROLLBACK'-Anweisung für den Fall, dass der SP nicht vollständig ausgeführt wird. Nur zum Schließen / Beenden der Transaktion.
J Cordero

1

Ich bin einmal auf diesen Fehler gestoßen, nachdem ich diese Anweisung aus meiner Transaktion weggelassen habe.

COMMIT TRANSACTION [MyTransactionName]

1

Stellen Sie sicher, dass Sie nicht mehrere Transaktionen in derselben Prozedur / Abfrage haben, von denen eine oder mehrere nicht festgeschrieben sind.

In meinem Fall hatte ich versehentlich eine BEGIN TRAN-Anweisung in der Abfrage


1

Dies kann auch davon abhängen, wie Sie den SP über Ihren C # -Code aufrufen. Wenn der SP einen Tabellentypwert zurückgibt, rufen Sie den SP mit ExecuteStoreQuery auf, und wenn der SP keinen Wert zurückgibt, rufen Sie den SP mit ExecuteStoreCommand auf


1

Vermeide das Benutzen

RETURN

Anweisung, wenn Sie verwenden

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

und

BEGIN, COMMIT & ROLLBACK

Anweisungen in gespeicherten SQL-Prozeduren


0

Meiner Meinung nach ist die akzeptierte Antwort in den meisten Fällen ein Overkill.

Die Fehlerursache ist häufig eine Nichtübereinstimmung von BEGIN und COMMIT, wie durch den Fehler deutlich angegeben. Dies bedeutet:

Begin
  Begin
    -- your query here
  End
commit

anstatt

Begin Transaction
  Begin
    -- your query here
  End
commit

Das Weglassen der Transaktion nach dem Start führt zu diesem Fehler!


-1

Wenn Sie eine Codestruktur haben, die Folgendes umfasst:

SELECT 151
RETURN -151

Dann benutze:

SELECT 151
ROLLBACK
RETURN -151
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.