web-dev-qa-db-fra.com

TSQL Try / Catch dans Transaction ou vice versa?

J'écris un script qui supprimera les enregistrements d'un certain nombre de tables, mais avant qu'il ne supprime, il doit renvoyer un nombre pour qu'un utilisateur confirme avant de valider.

Ceci est un résumé du script.

BEGIN TRANSACTION SCHEDULEDELETE
    BEGIN TRY
        DELETE   -- delete commands full SQL cut out
        DELETE   -- delete commands full SQL cut out
        DELETE   -- delete commands full SQL cut out
        PRINT 'X rows deleted. Please commit or rollback.' --calculation cut out.
    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

            ROLLBACK TRANSACTION SCHEDULEDELETE
            PRINT 'Error detected, all changes reversed.'
    END CATCH

--COMMIT TRANSACTION SCHEDULEDELETE --Run this if count correct.

--ROLLBACK TRANSACTION SCHEDULEDELETE --Run this if there is any doubt whatsoever.

C'est la première fois que j'écris une transaction, est-ce correct/meilleure pratique d'avoir le bloc TRY/CATCH à l'intérieur de la transaction ou la transaction doit-elle être à l'intérieur du bloc TRY?

Le facteur important dans ce script est que l'utilisateur doit valider manuellement la transaction.

22
Devasta

Ouvrez une transaction uniquement une fois que vous êtes dans le bloc TRY et juste avant l'instruction proprement dite, et validez-la immédiatement. N'attendez pas que votre contrôle aille à la fin du batch pour valider vos transactions.

Si quelque chose se passe mal pendant que vous êtes dans le bloc TRY et que vous avez ouvert une transaction, le contrôle passera au bloc CATCH. Restaurez simplement votre transaction là-bas et effectuez d'autres traitements d'erreur si nécessaire.

J'ai ajouté un petit chèque pour toute transaction ouverte en utilisant @@TRANCOUNT fonctionne avant d'annuler réellement la transaction. Cela n'a pas vraiment de sens dans ce scénario. Il est plus utile lorsque vous effectuez des vérifications de validation dans votre bloc TRY avant d'ouvrir une transaction, comme la vérification des valeurs de paramètres et d'autres éléments et la génération d'une erreur dans le bloc TRY si l'une des vérifications de validation échouer. Dans ce cas, le contrôle passera au bloc CATCH sans même ouvrir une transaction. Là, vous pouvez vérifier toute transaction ouverte et annulation s'il y en a. Dans votre cas, vous n'avez vraiment pas besoin de vérifier une transaction ouverte car vous n'entrerez pas dans le bloc CATCH sauf si quelque chose se passe mal à l'intérieur de votre transaction.

Ne demandez pas après avoir exécuté l'opération DELETE si elle doit être validée ou annulée; effectuez toutes ces validations avant d'ouvrir la transaction. Une fois qu'une transaction est ouverte, validez-la immédiatement et en cas d'erreur, faites le traitement des erreurs (vous faites du bon travail en obtenant des informations détaillées en utilisant presque toutes les fonctions d'erreur).

BEGIN TRY

  BEGIN TRANSACTION SCHEDULEDELETE
    DELETE   -- delete commands full SQL cut out
    DELETE   -- delete commands full SQL cut out
    DELETE   -- delete commands full SQL cut out
 COMMIT TRANSACTION SCHEDULEDELETE
    PRINT 'X rows deleted. Operation Successful Tara.' --calculation cut out.
END TRY

BEGIN CATCH 
  IF (@@TRANCOUNT > 0)
   BEGIN
      ROLLBACK TRANSACTION SCHEDULEDELETE
      PRINT 'Error detected, all changes reversed'
   END 
    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
END CATCH
46
M.Ali

En plus des bons conseils de M.ALi et du doyen ci-dessus, un peu d'aide pour ceux qui cherchent à utiliser le nouveau (er) paradigme TRY CATCH THROW dans SQL SERVER:

(Je n'ai pas pu trouver facilement la syntaxe complète, alors ajoutez-la ici)

Gist: ICI

Exemple de code de procédure stockée ici (de mon Gist):

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROC [dbo].[pr_ins_test]
@CompanyID INT
AS

SET NOCOUNT ON

BEGIN

    DECLARE @PreviousConfigID INT

    BEGIN TRY
        BEGIN TRANSACTION MYTRAN; -- Give the transaction a name
        SELECT 1/0  -- Generates divide by zero error causing control to jump into catch

        PRINT '>> COMMITING'
        COMMIT TRANSACTION MYTRAN;
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
        BEGIN 
            PRINT '>> ROLLING BACK'
            ROLLBACK TRANSACTION MYTRAN; -- The semi-colon is required (at least in SQL 2012)


        END
        THROW
    END CATCH
END
3
Sudhanshu Mishra

N'attendez jamais qu'un utilisateur final valide la transaction, sauf s'il s'agit d'une base de données en mode mono-utilisateur.

En bref, il s'agit de bloquer. Votre transaction prendra des verrous exclusifs sur les ressources mises à jour et conservera ces verrous jusqu'à la fin de la transaction (validée ou annulée). Personne ne pourra toucher ces rangées. Il existe différents problèmes si l'isolement d'instantané est utilisé avec le nettoyage du magasin de versions.

Mieux vaut d'abord émettre une requête de sélection pour déterminer un certain nombre de lignes éligibles, la présenter à l'utilisateur final, et après confirmation, effectuez la suppression réelle.

2
dean