SQL Server - Transaktionen werden bei Fehler zurückgesetzt?

192

Wir haben eine Client-App, auf der SQL auf einem SQL Server 2005 ausgeführt wird, z.

BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;

Es wird von einem langen String-Befehl gesendet.

Wenn eine der Einfügungen fehlschlägt oder ein Teil des Befehls fehlschlägt, setzt SQL Server die Transaktion zurück? Wenn es nicht zurückgesetzt wird, muss ich einen zweiten Befehl senden, um es zurückzusetzen?

Ich kann Angaben zur API und Sprache machen, die ich verwende, aber ich denke, SQL Server sollte für jede Sprache gleich reagieren.

Jonathanpeppers
quelle

Antworten:

204

Sie können set xact_abort onvor Ihre Transaktion stellen, um sicherzustellen, dass SQL im Fehlerfall automatisch zurückgesetzt wird.

Greg B.
quelle
1
Funktioniert dies unter MS SQL 2K und höher? Dies scheint die einfachste Lösung zu sein.
Jonathanpeppers
1
Es erscheint in den Dokumenten für 2000, 2005 und 2008, also nehme ich ja an. Wir verwenden es im Jahr 2008.
8
Muss ich es ausschalten oder ist es pro Sitzung?
Marc
5
@Marc Der Umfang von xact_abortbefindet sich auf der Verbindungsebene.
Keith
2
@AlexMcMillan Die Anweisung DROP PROCEDURE ändert die Datenbankstruktur im Gegensatz zu INSERT, das nur mit den Daten arbeitet. Es kann also nicht in eine Transaktion eingeschlossen werden. Ich vereinfache es zu sehr, aber im Grunde ist es so.
Eksortso
195

Sie haben insofern Recht, als die gesamte Transaktion zurückgesetzt wird. Sie sollten den Befehl zum Zurücksetzen eingeben.

Sie können dies TRY CATCHwie folgt in einen Block einschließen

BEGIN TRY
    BEGIN TRANSACTION

        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);

    COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN --RollBack in case of Error

    -- you can Raise ERROR with RAISEERROR() Statement including the details of the exception
    RAISERROR(ERROR_MESSAGE(), ERROR_SEVERITY(), 1)
END CATCH
Raj Mehr
quelle
2
Ich mag die Lösung von DyingCactus besser, er muss nur eine Codezeile ändern. Wenn deins, wenn aus irgendeinem Grund besser (oder zuverlässiger), lass es mich wissen.
Jonathanpeppers
13
Mit dem try catch können Sie den Fehler erfassen (und möglicherweise beheben) und bei Bedarf eine benutzerdefinierte Fehlermeldung auslösen.
Raj More
10
"Capture and Log" häufiger als "Capture and Fix", würde ich denken.
Federbrecher
24
Die Syntax von RAISERROR ist zumindest in SQL Server 2008R2 und höher falsch. Die korrekte Syntax finden Sie unter msdn.microsoft.com/en-us/library/ms178592.aspx .
Eric J.
2
@BornToCode Um sicherzustellen, dass die Transaktion vorhanden ist. Nehmen wir an, Sie haben Ihre Transaktion unter bestimmten Bedingungen (in der try) zurückgesetzt, aber der Code schlägt danach fehl. Es gibt keine Transaktion mehr, aber Sie gehen immer noch in die catch.
Gabriel GM
42

Hier der Code zum Abrufen der Fehlermeldung beim Arbeiten mit MSSQL Server 2016:

BEGIN TRY
    BEGIN TRANSACTION 
        -- Do your stuff that might fail here
    COMMIT
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN

        DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
        DECLARE @ErrorSeverity INT = ERROR_SEVERITY()
        DECLARE @ErrorState INT = ERROR_STATE()

    -- Use RAISERROR inside the CATCH block to return error  
    -- information about the original error that caused  
    -- execution to jump to the CATCH block.  
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH
samwise
quelle
1
Ich musste DECLARE @Var TYPE; SET @Var = ERROR;für das Auslösen von Fehlern in SQL Server 2005 verwenden. Andernfalls funktioniert der obige Code zum Auslösen von Fehlern auch für ältere DBs. Der Versuch, einer lokalen Variablen einen Standardwert zuzuweisen, verursachte Probleme.
Jtlindsey
Sie können einen einfachen Wurf verwenden. anstelle von RAISERROR- und ERROR_ * -Deklarationen.
Rodzmkii
21

Aus dem MDSN-Artikel Steuern von Transaktionen (Datenbankmodul) .

Wenn in einem Stapel ein Laufzeitanweisungsfehler (z. B. eine Einschränkungsverletzung) auftritt, besteht das Standardverhalten im Datenbankmodul darin, nur die Anweisung zurückzusetzen, die den Fehler generiert hat. Sie können dieses Verhalten mit der Anweisung SET XACT_ABORT ändern. Nachdem SET XACT_ABORT ON ausgeführt wurde, führt jeder Laufzeitanweisungsfehler zu einem automatischen Rollback der aktuellen Transaktion. Kompilierungsfehler, wie z. B. Syntaxfehler, sind von SET XACT_ABORT nicht betroffen. Weitere Informationen finden Sie unter SET XACT_ABORT (Transact-SQL).

In Ihrem Fall wird die gesamte Transaktion zurückgesetzt, wenn eine der Einfügungen fehlschlägt.

Vitaly
quelle
3
Was brauchen wir, um mit Syntaxfehlern umzugehen? oder Fehler kompilieren? Wenn einer von ihnen passiert, sollte die gesamte Transaktion zurückgesetzt werden
MonsterMMORPG
Das Abfangen von Kompilierungs- / Syntaxfehlern ist das Ziel von SSDT-Projekten. :-)
Joe the Coder
10

Wenn einer der Einfügungen fehlschlägt oder ein Teil des Befehls fehlschlägt, setzt SQL Server die Transaktion zurück?

Nein, tut es nicht.

Wenn es nicht zurückgesetzt wird, muss ich einen zweiten Befehl senden, um es zurückzusetzen?

Sicher, sollten Sie ausgeben ROLLBACKstatt COMMIT.

Wenn Sie entscheiden möchten, ob die Transaktion festgeschrieben oder zurückgesetzt werden soll, sollten Sie den COMMITSatz aus der Anweisung entfernen , die Ergebnisse der Einfügungen überprüfen und dann entweder COMMIToder ROLLBACKabhängig von den Ergebnissen der Prüfung ausgeben .

Quassnoi
quelle
Wenn ich also eine Fehlermeldung erhalte, sagen Sie "Primärschlüsselkonflikt". Ich muss einen zweiten Anruf zum Rollback senden. Ich denke das macht Sinn. Was passiert, wenn ein netzwerkbezogener Fehler auftritt, z. B. wenn die Verbindung während einer sehr lang laufenden SQL-Anweisung getrennt wird?
Jonathanpeppers
2
Wenn eine Verbindung unterbrochen wird, unterbricht das zugrunde liegende Netzwerkprotokoll (z. B. Named Pipesoder TCP) die Verbindung. Wenn eine Verbindung unterbrochen wird, werden SQL Serveralle derzeit ausgeführten Befehle gestoppt und die Transaktion zurückgesetzt.
Quassnoi
1
Die Lösung von DyingCactus scheint also mein Problem zu beheben, danke für die Hilfe.
Jonathanpeppers
Wenn Sie bei einem Fehler abbrechen müssen , ist dies die beste Option.
Quassnoi