Quel est l'avantage d'utiliser «SET XACT_ABORT ON» dans une procédure stockée?

Réponses:

231

SET XACT_ABORT ONindique à SQL Server de restaurer l'intégralité de la transaction et d'abandonner le lot lorsqu'une erreur d'exécution se produit. Il vous couvre dans les cas comme un délai d'expiration de commande survenant sur l'application cliente plutôt que dans SQL Server lui-même (ce qui n'est pas couvert par le XACT_ABORT OFFparamètre par défaut ).

Étant donné qu'un délai d'expiration de la requête laissera la transaction ouverte, il SET XACT_ABORT ONest recommandé dans toutes les procédures stockées avec des transactions explicites (sauf si vous avez une raison spécifique de faire autrement) car les conséquences d'une application effectuant un travail sur une connexion avec une transaction ouverte sont désastreuses.

Il y a un très bon aperçu sur le blog de Dan Guzman ,

Ben Griswold
la source
41
alors pourquoi n'est-il pas activé par défaut?
Mike W
1
Est-ce que XACT_ABORT est toujours nécessaire si vous avez le BEGIN TRY- BEGIN CATCHet ROLLBACKavec le BEGIN CATCHbloc dans Sql?
user20358
1
@ user20358 BEGIN TRY- n'attrapera BEGIN CATCHpas des choses comme un délai d'expiration sur l'application cliente, et certaines erreurs SQL sont également inaccessibles, vous laissant avec une transaction ouverte là où vous ne vous attendez pas à une.
Tom Lint le
37

À mon avis, SET XACT_ABORT ON a été rendu obsolète par l'ajout de BEGIN TRY / BEGIN CATCH dans SQL 2k5. Avant les blocages d'exceptions dans Transact-SQL, il était vraiment difficile de gérer les erreurs et les procédures non équilibrées étaient trop courantes (procédures qui avaient un @@ TRANCOUNT différent à la sortie par rapport à l'entrée).

Avec l'ajout de la gestion des exceptions Transact-SQL, il est beaucoup plus facile d'écrire des procédures correctes garantissant un équilibre correct des transactions. Par exemple, j'utilise ce modèle pour la gestion des exceptions et les 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
go

Cela me permet d'écrire des procédures atomiques qui annulent uniquement leur propre travail en cas d'erreurs récupérables.

L'un des principaux problèmes auxquels les procédures Transact-SQL sont confrontées est la pureté des données : parfois, les paramètres reçus ou les données des tables sont tout simplement faux, ce qui entraîne des erreurs de clé en double, des erreurs de contrainte référentielle, des erreurs de contrainte de vérification, etc. Après tout, c'est exactement le rôle de ces contraintes, si ces erreurs de pureté des données étaient impossibles et toutes prises en compte par la logique métier, les contraintes seraient toutes obsolètes (exagération dramatique ajoutée pour effet). Si XACT_ABORT est activé, toutes ces erreurs entraînent la perte de l'ensemble de la transaction, par opposition à la possibilité de coder des blocs d'exceptions qui gèrent l'exception de manière appropriée. Un exemple typique est d'essayer de faire un INSERT et de revenir à une UPDATE en cas de violation PK.

Remus Rusanu
la source
9
Sauf pour les délais d'expiration du client ... et à mon avis, SET XACT_ABORT est plus efficace dans SQL 2005 car le comportement est plus prévisible: beaucoup moins d'erreurs d'abandon de lots.
gbn le
7
Je suis un peu d'accord, mais je prévois ma gestion des erreurs autour de toutes les éventualités, car je sais que je serai blâmé en tant que développeur DBA si un délai de commande se produit.
gbn
4
@RemusRusanu Comment géreriez-vous autrement une opération de base de données synchrone de longue durée?
Ian Boyd
5
La documentation MSDN indique: «XACT_ABORT doit être défini sur ON pour les instructions de modification de données dans une transaction implicite ou explicite par rapport à la plupart des fournisseurs OLE DB, y compris SQL Server. Le seul cas où cette option n'est pas requise est si le fournisseur prend en charge les transactions imbriquées.» msdn.microsoft.com/en-us/library/ms188792(v=sql.120).aspx
Nathan
4
"À mon avis, SET XACT_ABORT ON a été rendu obsolète par l'ajout de BEGIN TRY / BEGIN CATCH" - Je vous entends, mais veuillez consulter sommarskog.se/error_handling/Part1.html
Ingénieur inversé
22

Citant MSDN :

Lorsque SET XACT_ABORT est activé, si une instruction Transact-SQL génère une erreur d'exécution, la transaction entière est terminée et annulée. Lorsque SET XACT_ABORT est désactivé, dans certains cas, seule l'instruction Transact-SQL qui a généré l'erreur est annulée et la transaction continue le traitement.

En pratique, cela signifie que certaines des instructions peuvent échouer, laissant la transaction «partiellement terminée», et il se peut qu'il n'y ait aucun signe de cet échec pour un appelant.

Un exemple simple:

INSERT INTO t1 VALUES (1/0)    
INSERT INTO t2 VALUES (1/1)    
SELECT 'Everything is fine'

Ce code s'exécuterait «avec succès» avec XACT_ABORT OFF, et se terminera par une erreur avec XACT_ABORT ON («INSERT INTO t2» ne sera pas exécuté et une application client déclenchera une exception).

Comme approche plus flexible, vous pouvez vérifier @@ ERROR après chaque instruction (old school), ou utiliser les blocs TRY ... CATCH (MSSQL2005 +). Personnellement, je préfère activer XACT_ABORT chaque fois qu'il n'y a aucune raison de gérer les erreurs avancées.

VladV
la source
8

En ce qui concerne les délais d'expiration des clients et l'utilisation de XACT_ABORT pour les gérer, à mon avis, il y a au moins une très bonne raison d'avoir des délais d'expiration dans les API clientes comme SqlClient, et c'est pour protéger le code de l'application cliente des blocages survenant dans le code du serveur SQL. Dans ce cas, le code client n'a aucun défaut, mais doit se protéger contre le blocage pour toujours attendre que la commande se termine sur le serveur. Donc, à l'inverse, si des délais d'expiration du client doivent exister pour protéger le code client, XACT_ABORT ON doit également protéger le code serveur des abandons du client, au cas où le code serveur prend plus de temps à s'exécuter que le client est prêt à attendre.

ionutm
la source
1

Il est utilisé dans la gestion des transactions pour garantir que toute erreur entraîne l'annulation de la transaction.

Dan Diplo
la source