Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Comment gérer les erreurs dans les transactions imbriquées SQL Server

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