Transaktion und Try-Catch im SQL Server-Job

9

Wir haben DML-Operationen in jedem Schritt eines SQL Server-Jobs. Um sicherzustellen, dass das Update / Insert zurückgesetzt wird, falls etwas schief geht, habe ich die Datenänderungen für jeden Schritt in TRY CATCHund TRANSACTIONBlöcke eingeschlossen:

BEGIN TRY
    BEGIN TRANSACTION

        [[INSERT/update statements]] ...

    IF @@TRANCOUNT > 0
    BEGIN
        COMMIT TRANSACTION
        PRINT 'Successful.'
    END

END TRY

BEGIN CATCH
    SELECT
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage

    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRANSACTION
        PRINT 'Unsuccessful.'
    END
END CATCH

Stellt es sicher, dass die Datenmanipulationen im Falle von Fehlern zurückgesetzt werden? Oder sollten andere Überlegungen berücksichtigt werden?

Wäre dies besser (mithilfe von Konfigurationen usw.)?

Danke.

Himmel
quelle

Antworten:

7

Ich würde eher ein Muster wie das aus Ausnahmebehandlung und verschachtelte Transaktionen empfehlen :

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

Dieses Muster überprüft den XACT_STATE()Catch-Block, um sich vor nicht festschreibbaren Transaktionen zu schützen :

Nicht festschreibbare Transaktionen und XACT_STATE
Wenn ein in einem TRY-Block generierter Fehler dazu führt, dass der Status der aktuellen Transaktion ungültig wird, wird die Transaktion als nicht festschreibbare Transaktion klassifiziert. Ein Fehler, der normalerweise eine Transaktion außerhalb eines TRY-Blocks beendet, führt dazu, dass eine Transaktion in einen nicht festschreibbaren Zustand wechselt, wenn der Fehler innerhalb eines TRY-Blocks auftritt. Eine nicht festschreibbare Transaktion kann nur Lesevorgänge oder eine ROLLBACK-TRANSAKTION ausführen. Die Transaktion kann keine Transact-SQL-Anweisungen ausführen, die eine Schreiboperation oder eine COMMIT-TRANSAKTION generieren würden. Die Funktion XACT_STATE gibt den Wert -1 zurück, wenn eine Transaktion als nicht festschreibbare Transaktion klassifiziert wurde. Wenn ein Stapel beendet ist, setzt das Datenbankmodul alle aktiven nicht festschreibbaren Transaktionen zurück. Wenn beim Eintritt der Transaktion in einen nicht festschreibbaren Zustand keine Fehlermeldung gesendet wurde, Nach Abschluss des Stapels wird eine Fehlermeldung an die Clientanwendung gesendet. Dies zeigt an, dass eine nicht festschreibbare Transaktion erkannt und zurückgesetzt wurde.

Ihr Code sucht @@TRANCOUNTan Stellen, an denen er nicht 0 sein kann. Er verwendet eine Mischung aus informativen PRINT-Nachrichten und SELECT-Ergebnismengen, um den Erfolg zu kommunizieren. Er behandelt keine Fehler, die behoben werden können. Im Idealfall sollten die Ausnahmen an den Client weitergegeben werden, in diesem Fall an den Agent-Job (dh Ihr Fang sollte erneut ausgelöst werden).

Remus Rusanu
quelle
Vielen Dank für Ihre hilfreiche Antwort und fantastische Website! Ich frage mich jedoch, ob ich dieses Muster immer noch mit einer einfachen DML-Anweisung (nicht mit einem gespeicherten Prozess) verwenden kann. Müssen wir die Transaktion auch wie folgt speichern? (Ich habe keinen Store-Proc zum Verwenden): Speichern Sie die Transaktion usp_my_procedure_name;
Sky
2

Was du hast, sieht für mich gut aus. Ich würde natürlich vorschlagen, etwas mit den Informationen zu tun, nachdem Sie die Transaktion zurückgesetzt haben, z. B. sie in ein Protokoll zu schreiben.

Datagod
quelle
1
Vielen Dank für Ihre Antwort. Können Sie mir bitte einen Hinweis geben, wie ich sie in ein Protokoll schreiben kann?
Sky
3
Wenn Sie Fehler oder Daten in eine Protokolltabelle schreiben möchten, kopieren Sie vor dem Rollback die gewünschten Daten in eine Tabellenvariable (es ist wichtig, dass Sie eine Tabellenvariable verwenden, eine temporäre Tabelle wird zurückgesetzt.) Und führen Sie dann aus Fügen Sie dann die Daten aus der Tabellenvariablen in die Protokollierungstabelle ein.
HLGEM