web-dev-qa-db-fra.com

Meilleures pratiques pour valider une transaction dans SQL Server où TRY CATCH est utilisé

Dans un bloc de code SQL Server, quel est le meilleur endroit pour placer la transaction de validation? A l'intérieur du bloc try catch ou à l'extérieur?.

Par exemple, l'option A ou l'option B est-elle la bonne approche ou s'agit-il de choix subjectifs?

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

Dans l'option B, existe-t-il une possibilité qu'une erreur se produise lors de sa validation en dehors de TRY-CATCH bloquer ?

4
user20358

L'option A est le bon choix. Il est possible que toutes les instructions d'une transaction fonctionnent, puis le COMMIT réel échoue, vous gardez donc le COMMIT dans votre bloc TRY afin que toute défaillance du COMMIT soit interceptée et que vous puissiez gérer avec élégance cette erreur et cette restauration.

Voir cette réponse sur SE. Dans votre exemple de code, si nous supposons que d'autres utilisateurs exécutent cette procédure simultanément, il se peut que le bloc TRY se termine avec succès, puis échoue sur COMMIT en raison des modifications apportées par d'autres sessions.

3
HandyD

Le meilleur moyen que j'ai trouvé pour le faire est le code suivant:

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

Notez l'utilisation de XACT_ABORT pour garantir que les erreurs sont détectées efficacement, le fait que les instructions BEGIN et COMMIT se trouvent dans le bloc TRY et WHILE pour @@ Trancount - cela devrait garantir que les transactions imbriquées sont annulées (pas toujours applicables)

L'instruction THROW peut également remplacer RAISERROR pour les versions SQL supérieures à 2012, pour relancer l'exception/l'erreur interceptée.

Comme Dan Guzman l'indique dans son commentaire, XACT_ABORT est utile pour détecter les erreurs que la construction TRY/CATCH ne fera pas, y compris les délais d'expiration, les erreurs de classement d'exécution, etc.

1
George.Palacios