Transaction et Try-catch dans le travail SQL Server

9

Nous avons des opérations DML à chaque étape d'un travail SQL Server. Pour assurer la mise à jour quelque chose / insertion sera annulée en cas de problème, je l' ai enveloppé les modifications de données de chaque étape dans TRY CATCHet TRANSACTIONblocs:

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

Cela garantit-il que les manipulations de données seront annulées en cas d'erreur (s)? Ou d'autres considérations devraient-elles être prises en compte?

Serait-il une meilleure façon de le faire (en utilisant des configurations, etc.)?

Je vous remercie.

Ciel
la source

Réponses:

7

Je recommanderais plutôt un modèle comme celui de la gestion des exceptions et des transactions imbriquées :

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

Ce modèle vérifie le XACT_STATE()dans le bloc catch pour se prémunir contre les transactions non engageables :

Transactions non validables et XACT_STATE
Si une erreur générée dans un bloc TRY entraîne l'invalidation de l'état de la transaction en cours, la transaction est classée comme transaction non validable. Une erreur qui met normalement fin à une transaction en dehors d'un bloc TRY fait passer une transaction dans un état non validable lorsque l'erreur se produit à l'intérieur d'un bloc TRY. Une transaction non validable ne peut effectuer que des opérations de lecture ou une TRANSACTION ROLLBACK. La transaction ne peut exécuter aucune instruction Transact-SQL qui générerait une opération d'écriture ou une COMMIT TRANSACTION. La fonction XACT_STATE renvoie une valeur de -1 si une transaction a été classée comme transaction non validable. Lorsqu'un lot se termine, le moteur de base de données annule toutes les transactions non validables actives. Si aucun message d'erreur n'a été envoyé lorsque la transaction est entrée dans un état non validable, une fois le lot terminé, un message d'erreur sera envoyé à l'application cliente. Cela indique qu'une transaction non validable a été détectée et annulée.

Votre code recherche des @@TRANCOUNTendroits où il ne peut pas être 0, il utilise un mélange de messages d'information PRINT et de jeux de résultats SELECT pour communiquer le succès, il ne gère pas les erreurs récupérables. Idéalement, les exceptions devraient se propager au client, dans ce cas au travail de l'agent (c'est-à-dire que votre capture devrait être relancée).

Remus Rusanu
la source
Merci pour votre réponse utile et votre site Web fantastique! Cependant, je me demande si je peux toujours utiliser ce modèle avec une simple instruction DML (pas un proc stocké)? Faut-il également enregistrer la transaction comme ci-dessous? (Je n'ai pas de proc de magasin à utiliser): enregistrer la transaction usp_my_procedure_name;
Sky
2

Ce que tu as me semble bien. Je suggérerais de faire quelque chose avec les informations bien sûr après avoir annulé la transaction, par exemple l'écrire dans un journal.

datagod
la source
1
Merci pour votre réponse, pourriez-vous me donner un indice sur la façon de l'écrire dans un journal?
Sky
3
Si vous souhaitez écrire des erreurs ou des données dans une table de journal, puis avant de faire la restauration, copiez les données que vous souhaitez dans une variable de table (il est important que vous utilisiez une variable de table, une table temporaire sera annulée.) Puis effectuez la restauration, puis insérez les données de la variable de table dans la table de journalisation.
HLGEM