225 votes

SQL Server - les transactions sont annulées en cas d'erreur ?

Nous avons une application client qui exécute un certain SQL sur un serveur SQL 2005, tel que le suivant :

BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;

Il est envoyé par une longue chaîne de commande.

Si l'une des insertions échoue, ou si une partie de la commande échoue, le serveur SQL annule-t-il la transaction ? S'il n'annule pas la transaction, dois-je envoyer une deuxième commande pour l'annuler ?

Je peux vous donner des détails sur l'API et la langue que j'utilise, mais je pense que le serveur SQL devrait répondre de la même manière pour n'importe quelle langue.

0 votes

235voto

DyingCactus Points 15843

Vous pouvez mettre set xact_abort on avant votre transaction pour s'assurer que sql revient automatiquement en arrière en cas d'erreur.

1 votes

Cela fonctionnera-t-il sur MS SQL 2K et supérieur ? Cela semble être la solution la plus simple.

1 votes

Il apparaît dans les documents pour 2000, 2005 et 2008, donc je suppose que oui. Nous l'utilisons en 2008.

0 votes

Existe-t-il une option permettant de revenir en arrière en cas de dépassement du délai d'attente du réseau ou d'erreurs de réseau en général ? Ou cette option gère-t-elle tout en même temps ?

234voto

Raj More Points 22358

Vous avez raison de dire que l'ensemble de la transaction sera annulée. Vous devez émettre la commande pour l'annuler.

Vous pouvez l'envelopper dans un TRY CATCH comme suit

BEGIN TRY
    BEGIN TRANSACTION

        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);

    COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN --RollBack in case of Error

    -- <EDIT>: From SQL2008 on, you must raise error messages as follows:
    DECLARE @ErrorMessage NVARCHAR(4000);  
    DECLARE @ErrorSeverity INT;  
    DECLARE @ErrorState INT;  

    SELECT   
       @ErrorMessage = ERROR_MESSAGE(),  
       @ErrorSeverity = ERROR_SEVERITY(),  
       @ErrorState = ERROR_STATE();  

    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);  
    -- </EDIT>
END CATCH

3 votes

Je préfère la solution de DyingCactus, il n'y a qu'une ligne de code à modifier. Si la vôtre est pour une raison quelconque meilleure (ou plus fiable), faites-le moi savoir.

17 votes

La capture try vous permet de capturer (et éventuellement de corriger) l'erreur et d'afficher un message d'erreur personnalisé si nécessaire.

0 votes

Puisque nous ne nous soucions pas vraiment de gérer l'erreur spécifiquement dans SQL, je pense que nous allons suivre la solution de DyingCactus.

57voto

samwise Points 566

Voici le code permettant d'obtenir le message d'erreur avec MSSQL Server 2016 :

BEGIN TRY
    BEGIN TRANSACTION 
        -- Do your stuff that might fail here
    COMMIT
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN

        DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
        DECLARE @ErrorSeverity INT = ERROR_SEVERITY()
        DECLARE @ErrorState INT = ERROR_STATE()

    -- Use RAISERROR inside the CATCH block to return error  
    -- information about the original error that caused  
    -- execution to jump to the CATCH block.  
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH

1 votes

J'ai dû utiliser DECLARE @Var TYPE; SET @Var = ERROR; pour soulever des erreurs dans sql server 2005. Sinon, le code ci-dessus pour lever les erreurs fonctionne aussi pour les bases de données plus anciennes. Essayer d'assigner une valeur par défaut à une variable locale est la cause du problème.

1 votes

Vous pouvez utiliser un simple THROW ; au lieu des déclarations RAISERROR et ERROR_*.

0 votes

Alors que nous pourrions utiliser 'set xact_abort on', cela permet plus de contrôle dans l'événement et est beaucoup plus lisible.

27voto

Vitaly Points 61

Extrait de l'article du MDSN, Contrôle des transactions (moteur de base de données) .

Si une erreur d'instruction d'exécution (telle qu'une violation de contrainte) se produit dans un lot, le comportement par défaut du moteur de base de données consiste à annuler uniquement l'instruction qui a généré l'erreur. Vous pouvez modifier ce comportement à l'aide de l'instruction SET XACT_ABORT. Après l'exécution de l'instruction SET XACT_ABORT ON, toute erreur d'instruction d'exécution entraîne un retour en arrière automatique de la transaction en cours. Les erreurs de compilation, telles que les erreurs de syntaxe, ne sont pas affectées par SET XACT_ABORT. Pour plus d'informations, voir SET XACT_ABORT (Transact-SQL).

Dans votre cas, la transaction complète sera annulée si l'une des insertions échoue.

3 votes

Que devons-nous faire pour gérer les erreurs de syntaxe ? ou les erreurs de compilation ? si l'une d'entre elles se produit, la transaction entière doit être annulée.

0 votes

Les projets SSDT sont faits pour corriger les erreurs de compilation/syntaxe :-)

12voto

Quassnoi Points 191041

Si l'une des insertions échoue, ou si une partie de la commande échoue, le serveur SQL annule-t-il la transaction ?

Non, ce n'est pas le cas.

S'il ne fait pas marche arrière, dois-je envoyer une deuxième commande pour le faire ?

Bien sûr, vous devriez émettre ROLLBACK au lieu de COMMIT .

Si vous voulez décider de valider ou d'annuler la transaction, vous devez supprimer l'option COMMIT de l'instruction, vérifiez les résultats des insertions, puis émettez l'une des deux phrases suivantes COMMIT o ROLLBACK en fonction des résultats de la vérification.

0 votes

Donc, si j'obtiens une erreur, par exemple "Primary key conflict", je dois envoyer un deuxième appel au rollback ? Je suppose que c'est logique. Que se passe-t-il s'il y a une erreur liée au réseau, par exemple si la connexion est coupée pendant l'exécution d'une instruction SQL très longue ?

2 votes

Lorsqu'une connexion est interrompue, le protocole de réseau sous-jacent (par exemple, le protocole d'accès à l'Internet) est désactivé. Named Pipes o TCP ) rompt la connexion. Lorsqu'une connexion est rompue, SQL Server arrête toutes les commandes en cours d'exécution et annule la transaction.

1 votes

La solution de DyingCactus semble résoudre mon problème, merci de votre aide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X