Dans cet article, nous allons explorer SQL Server Nested Transactions, un bloc de transaction avec une ou plusieurs transactions.
L'image décrit un modèle simple de la transaction imbriquée.
La transaction interne est une procédure stockée composée de blocs de transaction. MSDN recommande de « garder les transactions aussi courtes que possible », ce qui est totalement opposé à la première approche. À mon avis, je ne recommande pas d'utiliser des transactions imbriquées. Pourtant, nous devons parfois les utiliser pour résoudre certains problèmes commerciaux.
Ainsi, nous allons comprendre :
- Que se passe-t-il lorsqu'une transaction externe est annulée ou validée ?
- Que se passe-t-il lorsqu'une transaction interne est annulée ou validée ?
- Comment gérer les erreurs de transactions imbriquées ?
Pour commencer, nous allons créer un tableau de démonstration et tester les cas possibles.
USE AdventureWorks -----Create Demo Table---- CREATE TABLE CodingSightDemo (NumberValue VARCHAR(20))
Cas 1 :les transactions externes et internes sont validées.
TRUNCATE TABLE CodingSightDemo --<*************OUTHER TRANSACTION START*************> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') COMMIT TRAN --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') COMMIT TRAN --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
Dans ce cas, tous les enregistrements sont correctement insérés dans la table. Nous avons supposé que chaque instruction INSERT ne renvoyait pas d'erreur.
Cas 2 :la transaction externe est annulée , la transaction interne est engagée .
TRUNCATE TABLE CodingSightDemo --<*************OUTHER TRANSACTION START*************> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') COMMIT TRAN --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') rollback TRAN --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
Comme vous pouvez le voir, les enregistrements ne sont pas insérés dans la table car la transaction interne fait partie de la transaction externe. Pour cette raison, la transaction interne est annulée.
Cas 3 :la transaction externe est engagée , la transaction interne est annulée .
TRUNCATE TABLE CodingSightDemo --<*************OUTHER TRANSACTION START*************> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') ROLLBACK TRAN --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') COMMIT TRAN --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
Dans ce cas, nous avons obtenu une erreur et inséré la dernière instruction dans la table. En conséquence, certaines questions se posent :
- Pourquoi avons-nous obtenu une erreur ?
- Pourquoi la dernière instruction INSERT a-t-elle été ajoutée au tableau ?
En règle générale, l'instruction ROLLBACK TRAN annule toutes les transactions ouvertes exécutées dans la session en cours. Nous ne pouvons pas écrire une requête car elle renverra une erreur.
BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') ROLLBACK TRAN ROLLBACK TRAN
Nous examinerons comment cette règle peut affecter notre cas. L'instruction ROLLBACK TRAN annule les transactions internes et externes. Pour cette raison, nous obtenons une erreur lors de l'exécution de l'instruction COMMIT TRAN car il n'y a pas de transactions ouvertes.
Ensuite, nous ajouterons une instruction de gestion des erreurs à cette requête et la modifierons en fonction de l'approche de la programmation défensive (comme l'indique Wikipédia :la programmation défensive est une forme de conception défensive destinée à assurer le fonctionnement continu d'un logiciel dans des circonstances imprévues). Lorsque nous écrivons une requête sans nous soucier de la gestion des erreurs et obtenons une erreur, nous pouvons être confrontés à la corruption de l'intégrité des données.
Avec le prochain script, nous utiliserons des points de sauvegarde. Ils marquent un point dans la transaction et si vous le souhaitez, vous pouvez annuler toutes les instructions DML (Data Manipulation Language) au point marqué.
BEGIN TRY BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> SAVE TRANSACTION innerTRAN BEGIN TRY BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') COMMIT TRAN END TRY BEGIN CATCH IF XACT_STATE() <> 0 BEGIN ROLLBACK TRANSACTION innerTRAN PRINT 'Roll back occurs for inner tran' END IF XACT_STATE() <> 0 BEGIN COMMIT TRAN PRINT 'Commit occurs for firt open tran' END END CATCH --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') COMMIT TRAN END TRY BEGIN CATCH BEGIN IF XACT_STATE() <> 0 ROLLBACK TRAN PRINT 'Roll back occurs for outer tran' END END CATCH --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
Cette requête gère l'erreur lorsque la transaction interne reçoit une erreur. En outre, les transactions externes sont validées avec succès. Cependant, dans certains cas, si la transaction interne reçoit une erreur, la transaction externe doit être annulée. Dans ce cas, nous utiliserons une variable locale qui conservera et transmettra la valeur de l'état d'erreur de la requête interne. Nous allons concevoir la requête externe avec cette valeur de variable et la requête sera la suivante.
--<*************OUTHER TRANSACTION START*************> DECLARE @innertranerror as int=0 BEGIN TRY BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> SAVE TRANSACTION innerTRAN BEGIN TRY BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') COMMIT TRAN END TRY BEGIN CATCH IF XACT_STATE() <> 0 BEGIN SET @innertranerror=1 ROLLBACK TRANSACTION innerTRAN PRINT 'Roll back occurs for inner tran' END IF XACT_STATE() <> 0 BEGIN COMMIT TRAN PRINT 'Commit occurs for firt open tran' END END CATCH --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') if @innertranerror=0 BEGIN COMMIT TRAN END IF @innertranerror=1 BEGIN ROLLBACK TRAN END END TRY BEGIN CATCH BEGIN IF XACT_STATE() <> 0 ROLLBACK TRAN PRINT 'Roll back occurs for outer tran' END END CATCH --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
Conclusion
Dans cet article, nous avons exploré les transactions imbriquées et analysé comment gérer les erreurs dans ce type de requête. La règle la plus importante concernant ce type de transaction est d'écrire des requêtes défensives car nous pouvons obtenir une erreur dans les transactions externes ou internes. Pour cette raison, nous devons concevoir un comportement de gestion des erreurs de la requête.
Références
Transactions imbriquées
ENREGISTRER LA TRANSACTION