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

Tutoriel sur les transactions SQL

En SQL, les transactions sont utilisées pour maintenir l'intégrité des données en garantissant qu'une séquence d'instructions SQL s'exécute complètement ou pas du tout.

Les transactions gèrent des séquences d'instructions SQL qui doivent être exécutées comme une seule unité de travail, de sorte que la base de données ne contienne jamais les résultats d'opérations partielles.

Lorsqu'une transaction apporte plusieurs modifications à la base de données, soit toutes les modifications réussissent lorsque la transaction est validée, soit toutes les modifications sont annulées lorsque la transaction est annulée.

Quand utiliser une transaction ?

Les transactions sont primordiales dans les situations où l'intégrité des données serait menacée en cas d'échec de l'une quelconque d'une séquence d'instructions SQL.

Par exemple, si vous transférez de l'argent d'un compte bancaire à un autre, vous devrez déduire de l'argent d'un compte et l'ajouter à l'autre. Vous ne voudriez pas qu'il échoue à mi-parcours, sinon l'argent pourrait être débité d'un compte mais pas crédité sur l'autre.

Les raisons possibles de l'échec peuvent inclure des fonds insuffisants, un numéro de compte invalide, une panne matérielle, etc.

Alors vous ne faites pas veux être dans une situation où ça reste comme ça :

Debit account 1 (Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)

Ce serait vraiment mal. La base de données contiendrait des données incohérentes et l'argent disparaîtrait dans les airs. Ensuite, la banque perdrait un client (la banque perdrait probablement tous ses clients si cela continuait), et vous perdriez votre emploi.

Pour sauvegarder votre travail, vous pouvez utiliser une transaction qui ressemblerait à ceci :

START TRANSACTION
Debit account 1
Credit account 2
Record transaction in transaction journal
END TRANSACTION 

Vous pouvez écrire une logique conditionnelle à l'intérieur de cette transaction qui annule la transaction en cas de problème.

Par exemple, si quelque chose ne va pas entre le débit du compte 1 et le crédit du compte 2, toute la transaction est annulée.

Par conséquent, il n'y aurait que deux résultats possibles :

Debit account 1 (Not Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)

Ou :

Debit account 1 (Done)
Credit account 2 (Done)
Record transaction in transaction journal (Done)

Ceci est une représentation simplifiée, mais c'est une illustration classique du fonctionnement des transactions SQL. Les transactions SQL ont ACID.

Types de transactions

Les transactions SQL peuvent être exécutées dans les modes suivants.

Mode de transaction Description
Transaction de validation automatique Chaque relevé individuel est une transaction.
Transaction implicite Une nouvelle transaction est lancée implicitement lorsque la transaction précédente se termine, mais chaque transaction est explicitement terminée, généralement avec un COMMIT ou ROLLBACK déclaration en fonction du SGBD.
Transaction explicite Commence explicitement par une ligne telle que START TRANSACTION , BEGIN TRANSACTION ou similaire, selon le SGBD, et explicitement validé ou annulé avec les instructions pertinentes.
Transaction groupée Applicable uniquement à plusieurs ensembles de résultats actifs (MARS). Une transaction explicite ou implicite qui démarre sous une session MARS devient une transaction de portée par lots.

Les modes et options exacts disponibles peuvent dépendre du SGBD. Ce tableau décrit les modes de transaction disponibles dans SQL Server.

Dans cet article, nous nous concentrons principalement sur les transactions explicites.

Consultez Fonctionnement des transactions implicites dans SQL Server pour une discussion sur la différence entre les transactions implicites et la validation automatique.

Sytnax

Le tableau suivant décrit la syntaxe de base pour démarrer et terminer une transaction explicite dans certains des SGBD les plus populaires.

SGBD Syntaxe de transaction explicite
MySQL, MariaDB, PostgreSQL Les transactions explicites commencent par le START TRANSACTION ou BEGIN déclaration. COMMIT valide la transaction en cours, rendant ses modifications permanentes. ROLLBACK annule la transaction en cours, annulant ses modifications.
SQLite Les transactions explicites commencent par le BEGIN TRANSACTION déclaration et se terminent par le COMMIT ou ROLLBACK déclaration. Peut aussi se terminer par END déclaration.
SQL Server Les transactions explicites commencent par le BEGIN TRANSACTION déclaration et se terminent par le COMMIT ou ROLLBACK déclaration.
Oracle Les transactions explicites commencent par le SET TRANSACTION déclaration et se terminent par le COMMIT ou ROLLBACK déclaration.

Dans de nombreux cas, certains mots clés sont facultatifs lors de l'utilisation de transactions explicites. Par exemple, dans SQL Server et SQLite, vous pouvez simplement utiliser BEGIN (plutôt que BEGIN TRANSACTION ) et/ou vous pouvez terminer par COMMIT TRANSACTION (par opposition à simplement COMMIT ).

Il existe également divers autres mots-clés et options que vous pouvez spécifier lors de la création d'une transaction, consultez donc la documentation de votre SGBD pour la syntaxe complète.

Exemple de transaction SQL

Voici un exemple de transaction simple dans SQL Server :

BEGIN TRANSACTION
    DELETE OrderItems WHERE OrderId = 5006;
    DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION;

Dans ce cas, les informations de commande sont supprimées de deux tables. Les deux instructions sont traitées comme une seule unité de travail.

Nous pourrions écrire une logique conditionnelle dans notre transaction pour la faire revenir en arrière en cas d'erreur.

Nommer une transaction

Certains SGBD vous permettent de donner un nom à vos transactions. Dans SQL Server, vous pouvez ajouter le nom de votre choix après le BEGIN et COMMIT déclarations.

BEGIN TRANSACTION MyTransaction
    DELETE OrderItems WHERE OrderId = 5006;
    DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction;

Exemple de restauration de transaction SQL 1

Voici à nouveau l'exemple précédent, mais avec un peu de code supplémentaire. Le code supplémentaire est utilisé pour annuler la transaction en cas d'erreur. :

BEGIN TRANSACTION MyTransaction

  BEGIN TRY

    DELETE OrderItems WHERE OrderId = 5006;
    DELETE Orders WHERE OrderId = 5006;

    COMMIT TRANSACTION MyTransaction

  END TRY

  BEGIN CATCH

      ROLLBACK TRANSACTION MyTransaction

  END CATCH

Le TRY...CATCH L'instruction implémente la gestion des erreurs dans SQL Server. Vous pouvez inclure n'importe quel groupe d'instructions T-SQL dans un TRY bloc. Ensuite, si une erreur survient dans le TRY bloc, le contrôle est passé à un autre groupe d'instructions qui est enfermé dans un CATCH bloquer.

Dans ce cas, nous utilisons le CATCH bloc pour annuler la transaction. Étant donné que c'est dans le CATCH bloquer, la restauration n'a lieu qu'en cas d'erreur.

Exemple 2 de restauration de transactions SQL

Examinons de plus près la base de données dont nous venons de supprimer des lignes.

Dans l'exemple précédent, nous avons supprimé des lignes des Orders et OrderItems tables dans la base de données suivante :

Dans cette base de données, chaque fois qu'un client passe une commande, une ligne est insérée dans le Orders table, et une ou plusieurs lignes dans le OrderItems table. Le nombre de lignes insérées dans OrderItems dépend du nombre de produits différents commandés par le client.

De plus, s'il s'agit d'un nouveau client, une nouvelle ligne est insérée dans le champ Customers tableau.

Dans ce cas, les lignes doivent être insérées dans trois tables.

En cas d'échec, nous ne voudrions pas qu'une ligne soit insérée dans les Orders table mais pas de lignes correspondantes dans le OrderItems table. Cela se traduirait par une commande sans aucun article de commande. Fondamentalement, nous voulons que les deux tables soient complètement mises à jour ou rien du tout.

C'était la même chose lorsque nous avons supprimé les lignes. Nous voulions supprimer toutes les lignes ou aucune.

Dans SQL Server, nous pourrions écrire la transaction suivante pour le INSERT déclarations.

BEGIN TRANSACTION
    BEGIN TRY 
        INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
        VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');

        INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
        VALUES ( 5006, SYSDATETIME(), 1006 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 1, 1, 20, 25.99 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 2, 7, 120, 9.99 );

        COMMIT TRANSACTION;
        
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH

Cet exemple suppose qu'il existe une logique ailleurs qui détermine si oui ou non le client existe déjà dans la base de données.

Le client a pu être inséré en dehors de cette transaction :


INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');

BEGIN TRANSACTION
    BEGIN TRY 

        INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
        VALUES ( 5006, SYSDATETIME(), 1006 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 1, 1, 20, 25.99 );
        
        INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
        VALUES ( 5006, 2, 7, 120, 9.99 );

        COMMIT TRANSACTION;
        
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH

Si la transaction échouait, le client serait toujours dans la base de données (mais sans aucune commande). L'application devra vérifier si le client existe déjà avant d'effectuer la transaction.

Transaction SQL avec points de sauvegarde

Un point de sauvegarde définit un emplacement auquel une transaction peut revenir si une partie de la transaction est conditionnellement annulée. Dans SQL Server, nous spécifions un point de sauvegarde avec SAVE TRANSACTION savepoint_name (où nom_point_de_sauvegarde est le nom que nous donnons au point de sauvegarde).

Réécrivons l'exemple précédent pour inclure un point de sauvegarde :


BEGIN TRANSACTION
    INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
    VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
    SAVE TRANSACTION StartOrder;

    INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
    VALUES ( 5006, SYSDATETIME(), 1006 );
    
    INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
    VALUES ( 5006, 1, 1, 20, 25.99 );
    
    INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
    VALUES ( 5006, 2, 7, 120, 9.99 );
    ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;

Ici, nous avons défini un point de sauvegarde juste après le client INSERT déclaration. Plus tard dans la transaction, j'utilise le ROLLBACK pour demander à la transaction de revenir à ce point de sauvegarde.

Lorsque j'exécute cette instruction, le client est inséré, mais aucune information de commande n'est insérée.

Si une transaction est restaurée à un point de sauvegarde, elle doit se terminer avec plus d'instructions SQL si nécessaire et un COMMIT TRANSACTION déclaration, ou elle doit être complètement annulée en annulant l'intégralité de la transaction.

Si je déplace le ROLLBACK instruction de retour au précédent INSERT déclaration, comme celle-ci :

BEGIN TRANSACTION
    INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
    VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
    SAVE TRANSACTION StartOrder;

    INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
    VALUES ( 5006, SYSDATETIME(), 1006 );
    
    INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
    VALUES ( 5006, 1, 1, 20, 25.99 );
    ROLLBACK TRANSACTION StartOrder;
    
    INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
    VALUES ( 5006, 2, 7, 120, 9.99 );
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;

Cela produit une erreur de conflit de clé étrangère. Plus précisément, j'obtiens l'erreur suivante :

(1 row affected)
(1 row affected)
(1 row affected)
Msg 547, Level 16, State 0, Line 13
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_OrderItems_Orders". The conflict occurred in database "KrankyKranes", table "dbo.Orders", column 'OrderId'.
The statement has been terminated.
(1 row affected)

Cela s'est produit parce que, même si la commande avait déjà été insérée, cette opération a été annulée lorsque nous sommes revenus au point de sauvegarde. Ensuite, la transaction s'est déroulée jusqu'à son terme. Mais lorsqu'il a rencontré l'élément de commande final, il n'y avait pas de commande correspondante (car cela avait été annulé) et nous avons eu l'erreur.

Lorsque je vérifie la base de données, le client a été inséré, mais encore une fois, aucune des informations de commande n'a été insérée.

Vous pouvez référencer le même point de sauvegarde à plusieurs endroits dans la transaction si nécessaire.

En pratique, vous utiliseriez la programmation conditionnelle pour renvoyer la transaction à un savepont.

Transactions imbriquées

Vous pouvez également imbriquer des transactions dans d'autres transactions si nécessaire.

Comme ceci :

BEGIN TRANSACTION Transaction1;  
    UPDATE table1 ...;
    BEGIN TRANSACTION Transaction2;
        UPDATE table2 ...;
        SELECT * from table1;
    COMMIT TRANSACTION Transaction2;
    UPDATE table3 ...;
COMMIT TRANSACTION Transaction1;

Comme mentionné, la syntaxe exacte que vous utilisez pour créer une transaction dépendra de votre SGBD, alors consultez la documentation de votre SGBD pour une image complète de vos options lors de la création de transactions en SQL.