J'ai une procédure stockée qui n'exécute que 3 procédures stockées à l'intérieur. J'utilise seulement 1 paramètre pour stocker si le maître SP est réussi.
Si la première procédure stockée fonctionne correctement dans la procédure stockée principale, mais que la 2ème procédure stockée échoue, alors elle restaurera automatiquement tous les SP du maître SP ou dois-je faire une commande ?
Voici ma procédure:
CREATE PROCEDURE [dbo].[spSavesomename]
-- Add the parameters for the stored procedure here
@successful bit = null output
AS
BEGIN
begin transaction createSavebillinginvoice
begin Try
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN
EXEC [dbo].[spNewBilling1]
END
BEGIN
EXEC [dbo].[spNewBilling2]
END
BEGIN
EXEC [dbo].[spNewBilling3]
END
set @successful = 1
end Try
begin Catch
rollback transaction createSavesomename
insert into dbo.tblErrorMessage(spName, errorMessage, systemDate)
values ('spSavesomename', ERROR_MESSAGE(), getdate())
return
end Catch
commit transaction createSavesomename
return
END
GO
Étant donné uniquement le code affiché dans la question et en supposant qu'aucun des trois sous-processus n'a de gestion de transaction explicite, alors oui, une erreur dans l'un des trois sous-processus sera détectée et le ROLLBACK
dans le bloc CATCH
annulera tout le travail.
MAIS voici quelques points à noter sur les transactions (au moins dans SQL Server):
Il n'y a qu'une seule transaction réelle (la première), peu importe le nombre de fois que vous appelez BEGIN TRAN
BEGIN TRAN
, Qu'il soit nommé ou non, le compteur de transactions est incrémenté de 1.SELECT @@TRANCOUNT;
COMMIT
émises lorsque @@TRANCOUNT
Est à 2 ou au-dessus ne font que réduire, une à la fois, le compteur de transactions.COMMIT
soit émis lorsque le @@TRANCOUNT
Est à 1
Les points de sauvegarde permettent de créer un sous-ensemble de travail dans la transaction qui peut être annulée.
SAVE TRAN {save_point_name}
ROLLBACK {save_point_name}
. (plus d'informations ci-dessous)SAVE TRAN {save_point_name}
, Y compris tous les points de sauvegarde créés après la création de celui qui a été annulé (d'où le "nidification" ").SAVE TRAN
Initial ne peut pas être annulé, sauf en émettant un ROLLBACK
complet de la transaction entière.COMMIT
lorsque @@TRANCOUNT
Est à 2 ou plus, n'a aucun effet sur les points de sauvegarde (car encore une fois, les niveaux de transaction supérieurs à 1 n'existent pas en dehors de ce compteur).Vous ne pouvez pas valider des transactions nommées spécifiques. La transaction "nom", si elle est fournie avec COMMIT
, est ignorée et n'existe que pour la lisibilité.
Un ROLLBACK
émis sans nom annulera toujours TOUTES les transactions.
Un ROLLBACK
émis avec un nom doit correspondre à:
SAVE TRAN
N'a été appelé avec le même nom de transaction, cela annulera TOUTES les transactions.SAVE TRAN {save_point_name}
.SAVE TRAN
Avec son nom, alors chaque ROLLBACK de ce nom de transaction annulera chaque point de sauvegarde jusqu'à ce qu'il n'en reste plus. Après cela, un ROLLBACK émis de ce nom annulera TOUTES les transactions.Par exemple, supposons que les commandes suivantes ont été exécutées dans l'ordre indiqué:
BEGIN TRAN A -- @@TRANCOUNT is now 1
-- DML Query 1
SAVE TRAN A
-- DML Query 2
SAVE TRAN A
-- DML Query 3
BEGIN TRAN B -- @@TRANCOUNT is now 2
SAVE TRAN B
-- DML Query 4
Maintenant, si vous émettez (chacun des scénarios suivants est indépendant les uns des autres):
ROLLBACK TRAN B
Une fois: il annulera "DML Query 4". @@TRANCOUNT
Est toujours 2.ROLLBACK TRAN B
Deux fois: cela annulera "DML Query 4" puis une erreur car il n'y a pas de point de sauvegarde correspondant pour "B". @@TRANCOUNT
Est toujours 2.ROLLBACK TRAN A
Une fois: il annulera "DML Query 4" et "DML Query 3". @@TRANCOUNT
Est toujours 2.ROLLBACK TRAN A
Deux fois: il annulera "DML Query 4", "DML Query 3" et "DML Query 2". @@TRANCOUNT
Est toujours 2.ROLLBACK TRAN A
Trois fois: il annulera "DML Query 4", "DML Query 3" et "DML Query 2". Ensuite, il annulera toute la transaction (il ne restait plus que "DML Query 1"). @@TRANCOUNT
Est désormais à 0.COMMIT
une fois: @@TRANCOUNT
descend à 1.COMMIT
une fois puis ROLLBACK TRAN B
une fois: @@TRANCOUNT
descend à 1. Ensuite, il annulera "DML Query 4" (prouvant que COMMIT n'a rien fait). @@TRANCOUNT
Est toujours 1.Noms des transactions et noms des points d'enregistrement:
Une procédure stockée n'est pas, en soi, une transaction implicite. Chaque requête si aucune transaction explicite n'a été démarrée, est une transaction implicite. C'est pourquoi les transactions explicites autour des requêtes uniques ne sont pas nécessaires, sauf s'il peut y avoir une raison programmatique de faire un ROLLBACK
, sinon toute erreur dans la requête est une restauration automatique de cette requête.
Lors de l'appel d'une procédure stockée, elle doit quitter avec la valeur de @@TRANCOUNT
Étant la même que lors de son appel. Vous ne pouvez donc pas:
BEGIN TRAN
Dans le proc sans le valider, en attendant de le valider dans le processus appelant/parent.ROLLBACK
si une transaction explicite a été lancée avant l'appel du proc car elle renverra @@TRANCOUNT
À 0.Si vous quittez une procédure stockée avec un nombre de transactions supérieur ou inférieur à celui où elle a démarré, vous obtiendrez une erreur similaire à:
Msg 266, niveau 16, état 2, procédure YourProcName, ligne 0
Le nombre de transactions après EXECUTE indique un nombre différent d'instructions BEGIN et COMMIT. Nombre précédent = X, nombre actuel = Y.
Les variables de table, tout comme les variables régulières, ne sont pas liées par des transactions.
En ce qui concerne la gestion des transactions dans les procs qui peut être appelée indépendamment (et donc avoir besoin de la gestion des transactions) ou appeler à partir d'autres procs (donc pas besoin de la gestion des transactions): cela peut être accompli de deux manières différentes.
La façon dont je le gère depuis plusieurs années maintenant qui semble bien fonctionner est de ne faire que BEGIN
/COMMIT
/ROLLBACK
au niveau le plus externe. Les appels de sous-proc ignorent simplement les commandes de transaction. J'ai décrit ci-dessous ce que je mets dans chaque proc (enfin, chacun qui nécessite une gestion de transaction).
DECLARE @InNestedTransaction BIT;
Au lieu du simple BEGIN TRAN
, Faites:
IF (@@TRANCOUNT = 0)
BEGIN
SET @InNestedTransaction = 0;
BEGIN TRAN; -- only start a transaction if not already in one
END;
ELSE
BEGIN
SET @InNestedTransaction = 1;
END;
Au lieu du simple COMMIT
, faites:
IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
BEGIN
COMMIT;
END;
Au lieu du simple ROLLBACK
, faites:
IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
Cette méthode doit fonctionner de la même manière, que la transaction ait été lancée dans SQL Server ou qu'elle ait été lancée au niveau de la couche d'application.
Pour le modèle complet de cette gestion de transaction dans la construction TRY...CATCH
, Veuillez consulter ma réponse à la question DBA.SE suivante: Sommes-nous tenus de gérer la transaction en code C # ainsi qu'en procédure stockée .
Au-delà des "bases", il y a quelques nuances supplémentaires à prendre en compte:
Par défaut, les transactions ne sont, la plupart du temps, pas automatiquement annulées/annulées lorsqu'une erreur se produit. Ce n'est généralement pas un problème tant que vous gérez correctement les erreurs et appelez ROLLBACK
vous-même. Cependant, parfois les choses se compliquent, comme dans le cas d'erreurs d'abandon par lots, ou lors de l'utilisation de OPENQUERY
(ou des serveurs liés en général) et une erreur se produit sur le système distant. Alors que la plupart des erreurs peuvent être interceptées en utilisant TRY...CATCH
, Il y en a deux qui ne peuvent pas être interceptées de cette façon (cependant, je ne me souviens pas lesquelles pour le moment - recherche). Dans ces cas, vous devez utiliser SET XACT_ABORT ON
Pour annuler correctement la transaction.
SET XACT_ABORT ON oblige SQL Server à immédiatement annuler toute transaction (si une est active) et abandonner le lot si tout une erreur se produit. Ce paramètre existait avant SQL Server 2005, qui a introduit la construction TRY...CATCH
. Pour la plupart, TRY...CATCH
Gère la plupart des situations et rend donc la plupart du temps obsolète le besoin de XACT_ABORT ON
. Cependant, lorsque vous utilisez OPENQUERY
(et peut-être un autre scénario dont je ne me souviens pas pour le moment), vous devrez toujours utiliser SET XACT_ABORT ON;
.
À l'intérieur d'un déclencheur, XACT_ABORT
Est implicitement défini sur ON
. Cela provoque une erreur any dans le déclencheur pour annuler l'intégralité de l'instruction DML qui a déclenché le déclencheur.
Vous devez toujours avoir une gestion des erreurs appropriée, en particulier lorsque vous utilisez des transactions. La construction TRY...CATCH
, Introduite dans SQL Server 2005, fournit un moyen de gérer presque toutes les situations, une amélioration bienvenue par rapport au test de @@ERROR
Après chaque instruction, ce qui n'a pas beaucoup aidé à l'abandon de lots les erreurs.
TRY...CATCH
A cependant introduit un nouvel "état". Lorsque not utilisant la construction TRY...CATCH
, Si vous avez une transaction active et qu'une erreur se produit, plusieurs chemins peuvent être empruntés:
XACT_ABORT OFF
Et erreur d'abandon de l'instruction: la transaction est toujours active et le traitement se poursuit avec la prochaine instruction, le cas échéant.XACT_ABORT OFF
Et la plupart des erreurs d'abandon de lot: la transaction est toujours active et le traitement se poursuit avec le prochain lot, le cas échéant.XACT_ABORT OFF
Et certaines erreurs d'abandon de lot: la transaction est annulée et le traitement se poursuit avec le prochain lot, le cas échéant.XACT_ABORT ON
Et any erreur: la transaction est annulée et le traitement se poursuit avec le batch suivant, le cas échéant.
TOUTEFOIS, lors de l'utilisation de TRY...CATCH
, Les erreurs d'abandon de lot n'interrompent pas le lot, mais transfèrent le contrôle au bloc CATCH
. Lorsque XACT_ABORT
Est OFF
, la transaction sera toujours active la grande majorité du temps, et vous devrez COMMIT
, ou très probablement, ROLLBACK
. Mais lorsque vous rencontrez certaines erreurs d'abandon de lot (comme avec OPENQUERY
), ou lorsque XACT_ABORT
Est ON
, la transaction sera dans un nouvel état, "non engageable". Dans cet état, vous ne pouvez pas COMMIT
, ni effectuer aucune opération DML. Tout ce que vous pouvez faire est des instructions ROLLBACK
et SELECT
. Cependant, dans cet état "incontrôlable", la transaction a été annulée en cas d'erreur, et l'émission de ROLLBACK
n'est qu'une formalité, mais celle-ci doit être effectuée.
Une fonction, XACT_STATE , peut être utilisée pour déterminer si une transaction est active, non engageable ou n'existe pas. Il est recommandé (par certains, au moins) de vérifier cette fonction dans le bloc CATCH
pour déterminer si le résultat est -1
(C'est-à-dire non engageable) au lieu de tester si @@TRANCOUNT > 0
. Mais avec XACT_ABORT ON
, Cela devrait être le seul état possible, donc il semble que les tests pour @@TRANCOUNT > 0
Et XACT_STATE() <> 0
sont équivalents. D'un autre côté, lorsque XACT_ABORT
Est OFF
et qu'une Transaction est active, il est possible d'avoir un état de 1
Ou -1
Dans le bloc CATCH
, qui permet la possibilité d'émettre COMMIT
au lieu de ROLLBACK
(bien que je ne puisse pas penser à un cas où quelqu'un voudrait COMMIT
si la Transaction est validable). Plus d'informations et de recherches sur l'utilisation de XACT_STATE()
dans un bloc CATCH
avec XACT_ABORT ON
Se trouvent dans ma réponse à la question DBA.SE suivante: Dans quels cas une transaction peut être validée depuis l'intérieur du bloc CATCH lorsque XACT_ABORT est défini sur ON? . Veuillez noter qu'il existe un bogue mineur avec XACT_STATE()
qui le fait renvoyer faussement 1
Dans certains scénarios: XACT_STATE () renvoie 1 lorsqu'il est utilisé dans SELECT avec certaines variables système mais sans clause FROM
Remarques sur le code d'origine:
BEGIN
et END
autour de chaque EXEC
appelOui, si en raison d'une erreur de code d'annulation dans l'instruction catch de votre procédure stockée principale, elle s'exécutera, elle annulera toutes les opérations effectuées par n'importe quelle instruction directe ou via l'une de vos procédures stockées imbriquées.
Même si vous n'avez appliqué aucune transaction explicite dans vos procédures stockées imbriquées, ces procédures stockées utiliseront une transaction implicite et seront validées à la fin MAIS soit vous avez effectué une transaction explicite ou implicite dans des procédures stockées imbriquées, le moteur SQL Server l'ignorera et annule toutes les actions de ces procédures stockées imbriquées si la procédure stockée principale échoue et que la transaction est annulée.
Chaque fois que la transaction est validée ou annulée en fonction de l'action effectuée à la fin de la transaction la plus externe. Si la transaction externe est validée, les transactions internes imbriquées sont également validées. Si la transaction externe est annulée, toutes les transactions internes sont également annulées, que les transactions internes aient été ou non validées individuellement.
Pour référence http://technet.Microsoft.com/en-us/library/ms189336 (v = sql.105) .aspx