Quel est l’avantage de l’utilisation SET XACT_ABORT ON
dans une procédure stockée?
Quel est l’avantage de l’utilisation SET XACT_ABORT ON
dans une procédure stockée?
Réponses:
SET XACT_ABORT ON
indique à 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 OFF
paramètre par défaut ).
Étant donné qu'un délai d'expiration de la requête laissera la transaction ouverte, il SET XACT_ABORT ON
est 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 ,
BEGIN TRY
- BEGIN CATCH
et ROLLBACK
avec le BEGIN CATCH
bloc dans Sql?
BEGIN TRY
- n'attrapera BEGIN CATCH
pas 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.
À 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.
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.
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.