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.