it-swarm.com.de

Best Practices für das Festschreiben einer Transaktion in SQL Server, in der TRY CATCH verwendet wird

Was ist in einem SQL Server-Codeblock der beste Ort, um die Festschreibungstransaktion zu platzieren? Innerhalb des Try-Catch-Blocks oder außerhalb?.

Ist beispielsweise Option A oder Option B der richtige Ansatz oder handelt es sich um subjektive Entscheidungen?

Option A

CREATE PROCEDURE DummyProc 
BEGIN TRY
      BEGIN TRANSACTION
      INSERT sometable(a, b) VALUES (@a, @b)
      INSERT sometable(a, b) VALUES (@b, @a)
      COMMIT TRANSACTION
   END TRY
   BEGIN CATCH
      IF @@trancount > 0 ROLLBACK TRANSACTION
      DECLARE @msg nvarchar(2048) = error_message()  
      RAISERROR (@msg, 16, 1)
      RETURN 55555
   END CATCH   

Option B

CREATE PROCEDURE DummyProc 
BEGIN TRY
      BEGIN TRANSACTION
      INSERT sometable(a, b) VALUES (@a, @b)
      INSERT sometable(a, b) VALUES (@b, @a)

   END TRY
   BEGIN CATCH
      IF @@trancount > 0 ROLLBACK TRANSACTION
      DECLARE @msg nvarchar(2048) = error_message()  
      RAISERROR (@msg, 16, 1)
      RETURN 55555
   END CATCH
   IF @@trancount > 0  COMMIT TRANSACTION

Gibt es in Option B die Möglichkeit, dass ein Fehler auftritt, wenn ein Commit außerhalb des Blocks TRY-CATCH Ausgeführt wird?

4
user20358

Option A ist die richtige Wahl. Es ist möglich, dass alle Anweisungen in einer Transaktion funktionieren und dann das eigentliche COMMIT fehlschlägt. Sie behalten also das COMMIT in Ihrem TRY-Block, damit ein Fehler des COMMIT abgefangen wird, und können diesen Fehler und das Rollback ordnungsgemäß behandeln.

Siehe diese Antwort auf SE. Wenn wir in Ihrem Beispielcode davon ausgehen, dass andere Benutzer diese Prozedur gleichzeitig ausführen, kann es sein, dass der TRY-Block erfolgreich abgeschlossen wird und dann aufgrund von Änderungen, die von anderen Sitzungen vorgenommen wurden, im COMMIT fehlschlägt.

3
HandyD

Der beste Weg, dies zu tun, ist der folgende Code:

SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION

    /*
        Code goes here
    */

    COMMIT TRANSACTION
END TRY
BEGIN CATCH

    DECLARE @ErrorMessage NVARCHAR(4000);
    DECLARE @ErrorSeverity INT;
    DECLARE @ErrorState INT;

    SELECT 
        @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorState = ERROR_STATE();

    RAISERROR (@ErrorMessage, -- Message text.
               @ErrorSeverity, -- Severity.
               @ErrorState -- State.
               );

    -- If >= SQL 2012 replace all code in catch block above with
    -- THROW;

    WHILE @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRANSACTION;
    END

END CATCH

Beachten Sie die Verwendung von XACT_ABORT, um sicherzustellen, dass die Fehler effektiv abgefangen werden, die Tatsache, dass sich sowohl die BEGIN- als auch die COMMIT-Anweisungen im TRY-Block befinden, und das WHILE für @@ Trancount - dies sollte sicherstellen, dass verschachtelte Transaktionen zurückgesetzt werden (nicht immer zutreffend).

Die THROW-Anweisung kann auch RAISERROR für SQL-Versionen über 2012 ersetzen, um die abgefangene Ausnahme/den abgefangenen Fehler erneut auszulösen.

Wie Dan Guzman in seinem Kommentar feststellt, ist XACT_ABORT nützlich, um Fehler abzufangen, die das TRY/CATCH-Konstrukt nicht erkennt, einschließlich Zeitüberschreitungen, Laufzeitsortierungsfehlern usw.

1
George.Palacios