La commande BEGIN TRANSACTION (ou BEGIN selon le SGBD) démarre une transaction explicite qui regroupe plusieurs requêtes en une seule unité logique. Les modifications effectuées dans cette transaction restent invisibles aux autres utilisateurs jusqu’au COMMIT final. En cas d’erreur, ROLLBACK annule toutes les modifications effectuées depuis le BEGIN.
Sans transaction explicite, la plupart des SGBD fonctionnent en mode autocommit : chaque requête est validée immédiatement et de manière indépendante. Ce comportement provoque des incohérences lorsque plusieurs requêtes sont interdépendantes.
Table des matières
Syntaxe de base
BEGIN TRANSACTION marque le point de départ de la transaction. Toutes les requêtes qui suivent font partie de cette transaction jusqu’au COMMIT ou ROLLBACK. La syntaxe varie légèrement selon le SGBD.
| SGBD | Syntaxe |
|---|---|
| MySQL / MariaDB | START TRANSACTION ou BEGIN |
| PostgreSQL | BEGIN ou START TRANSACTION |
| SQL Server | BEGIN TRANSACTION |
Pour démarrer une transaction qui insère un client et valide immédiatement :
BEGIN TRANSACTION;
INSERT INTO clients (nom, email) VALUES ('Marie', 'marie@email.com');
UPDATE clients SET actif = 1 WHERE nom = 'Marie';
COMMIT;
Les deux opérations forment une unité atomique. Si l’une échoue, ROLLBACK annule les deux et COMMIT les rend définitives et visibles aux autres utilisateurs.
Comprendre l’autocommit
Par défaut, MySQL et PostgreSQL activent l’autocommit : chaque requête est une transaction indépendante validée automatiquement. Ce mode convient aux modifications unitaires simples, mais pas aux séquences multi-requêtes interdépendantes.
Voici ce qui se passe sans transaction explicite quand une erreur survient en milieu de séquence :
SET autocommit = 1; -- Mode par défaut MySQL
INSERT INTO clients VALUES ('Paul', 'paul@email.com'); -- Validé immédiatement
UPDATE clients SET actif = 1 WHERE nom = 'Paul'; -- Erreur ici !
-- Résultat : Paul est inséré mais inactif → état incohérent
L’insertion est déjà validé au moment où l’erreur survient. La base se retrouve dans un état incohérent car les deux opérations n’ont pas été traitées ensemble. Pour forcer les transactions explicites, désactiver l’autocommit :
SET autocommit = 0;
INSERT INTO clients VALUES ('Paul', 'paul@email.com');
UPDATE clients SET actif = 1 WHERE nom = 'Paul';
COMMIT; -- Les deux opérations sont validées ensemble
Avec autocommit = 0, aucune modification n’est validée tant que COMMIT n’est pas exécuté. Une erreur intermédiaire permet d’annuler l’ensemble avec ROLLBACK.
Exemples pratiques
Virement bancaire
Un virement débite un compte et crédite un autre. Ces deux opérations sont indissociables : l’une sans l’autre crée un déséquilibre comptable.
BEGIN TRANSACTION;
UPDATE comptes SET solde = solde - 750 WHERE id_client = 101;
UPDATE comptes SET solde = solde + 750 WHERE id_client = 202;
COMMIT;
Si une panne survient après la mise à jour, le ROLLBACK automatique (ou manuel) restaure les deux comptes à leur état initial.
BEGIN TRANSACTION;
UPDATE comptes SET solde = solde - 750 WHERE id_client = 101;
-- Panne réseau détectée
ROLLBACK; -- Le compte 101 retrouve son solde initial
Création d’un client et de sa première commande
Insérer un client et sa commande simultanément nécessite que les deux insertions réussissent ensemble. Si la commande échoue, le client ne doit pas exister non plus.
BEGIN TRANSACTION;
INSERT INTO clients (nom, email, actif) VALUES ('Dupont', 'dupont@ex.com', 1);
INSERT INTO commandes (client_id, montant, date) VALUES (LAST_INSERT_ID(), 250, NOW());
COMMIT;
LAST_INSERT_ID() récupère l’identifiant généré par le premier INSERT. Si le second INSERT échoue, ROLLBACK supprime aussi le client inséré.
Synchronisation stock et vente
Décrémente le stock et enregistre la vente en une seule transaction pour maintenir la cohérence entre les deux tables.
BEGIN TRANSACTION;
UPDATE produits SET stock = stock - 3 WHERE id = 45;
INSERT INTO ventes (produit_id, quantite, date) VALUES (45, 3, NOW());
COMMIT;
Si l’insertion dans la table des ventes échoue, le stock est restauré à sa valeur initiale.
Gestion des erreurs avec TRY/CATCH
En production, encapsuler la transaction dans un bloc TRY/CATCH garantit qu’un ROLLBACK est toujours exécuté en cas d’erreur. Sans ce mécanisme, une transaction non terminée reste ouverte et pose des verrous indéfinis sur les tables. Ce pattern s’applique principalement à SQL Server, qui dispose d’une syntaxe TRY/CATCH native :
BEGIN TRY
BEGIN TRANSACTION;
UPDATE comptes SET solde = solde - 1000 WHERE id = 101;
UPDATE comptes SET solde = solde + 1000 WHERE id = 202;
COMMIT;
END TRY
BEGIN CATCH
ROLLBACK;
PRINT 'Erreur : ' + ERROR_MESSAGE();
END CATCH;
Le bloc CATCH intercepte toute erreur SQL et exécute systématiquement le ROLLBACK. Le message d’erreur facilite le diagnostic en production.
Transactions imbriquées et @@TRANCOUNT
SQL Server suit le niveau d’imbrication des transactions avec la variable @@TRANCOUNT. Chaque BEGIN TRANSACTION incrémente ce compteur. COMMIT le décrémente, mais ne valide définitivement les données que lorsqu’il atteint zéro.
Vérifier @@TRANCOUNT avant un COMMIT critique évite les erreurs de validation hors transaction :
SELECT @@TRANCOUNT; -- 0 : aucune transaction active
BEGIN TRANSACTION; -- @@TRANCOUNT = 1
BEGIN TRANSACTION; -- @@TRANCOUNT = 2 (imbriquée)
COMMIT; -- @@TRANCOUNT = 1 (pas encore final)
COMMIT; -- @@TRANCOUNT = 0 (validation définitive)
Le premier COMMIT termine la transaction interne sans valider les données. Seul le second COMMIT rend les modifications visibles aux autres utilisateurs.
Niveaux d’isolation
BEGIN TRANSACTION accepte un niveau d’isolation qui contrôle la visibilité des modifications effectuées par d’autres transactions concurrentes. READ COMMITTED est le niveau par défaut sur la majorité des SGBD.
| Niveau | Lectures sales | Lectures non répétables | Lectures fantômes |
|---|---|---|---|
READ UNCOMMITTED | ✅ Possible | ✅ Possible | ✅ Possible |
READ COMMITTED | ❌ Impossible | ✅ Possible | ✅ Possible |
REPEATABLE READ | ❌ Impossible | ❌ Impossible | ✅ Possible |
SERIALIZABLE | ❌ Impossible | ❌ Impossible | ❌ Impossible |
Pour démarrer une transaction avec un niveau d’isolation précis :
-- PostgreSQL / SQL Server
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE comptes SET solde = solde - 500 WHERE id = 101;
COMMIT;
SERIALIZABLE garantit que la transaction s’exécute comme si elle était la seule active. Ce niveau est recommandé pour les opérations financières critiques.
SAVEPOINT
Cette commande SQL crée un point de restauration intermédiaire à l’intérieur d’une transaction. Il permet d’annuler partiellement des modifications sans abandonner toute la transaction. Voir l’article dédié à SAVEPOINT.
Pour annuler uniquement la seconde opération d’un virement sans annuler la première :
BEGIN TRANSACTION;
UPDATE comptes SET solde = solde - 300 WHERE id = 101;
SAVEPOINT avant_credit;
UPDATE comptes SET solde = solde + 300 WHERE id = 202;
-- Erreur détectée sur le crédit
ROLLBACK TO SAVEPOINT avant_credit;
COMMIT; -- Seul le débit du compte 101 est validé
Le débit du compte 101 est conservé. Seul le crédit du compte 202 est annulé.
Propriétés ACID
Les transactions SQL respectent les quatre propriétés ACID, qui garantissent la fiabilité des opérations en cas d’erreur ou de panne.
| Propriété | Définition | Exemple concret |
|---|---|---|
| Atomicité | Tout ou rien : toutes les opérations réussissent ou aucune n’est appliquée | Un virement annulé ne débite ni ne crédite aucun compte |
| Cohérence | La transaction fait passer la base d’un état valide à un autre état valide | Une contrainte de clé étrangère reste respectée après chaque transaction |
| Isolation | Chaque transaction s’exécute sans voir les modifications non validées des autres | Deux virements simultanés ne se lisent pas mutuellement avant COMMIT |
| Durabilité | Une fois validée, la transaction survit aux pannes et redémarrages | Un COMMIT réussi persiste même après un crash serveur |
Les SGBD relationnels comme MySQL (InnoDB), PostgreSQL et SQL Server implémentent les propriétés ACID nativement via des journaux de transactions (Write-Ahead Logging) et des mécanismes de verrouillage.
ACID vs BASE
Le modèle BASE s’applique aux bases NoSQL distribuées qui privilégient la disponibilité sur la cohérence stricte. Il ne s’applique pas aux transactions SQL classiques.
| Critère | ACID (SQL) | BASE (NoSQL distribué) |
|---|---|---|
| Cohérence | Immédiate et absolue | Progressive |
| Disponibilité | Conditionnelle à la stabilité | Prioritaire |
| Cas d’usage | Banque, ERP, e-commerce | Réseaux sociaux, logs temps réel |
Les SGBD modernes comme CockroachDB et YugabyteDB combinent le modèle ACID au niveau transactionnel avec une tolérance aux pannes réseau propre aux architectures distribuées.
Bonnes pratiques
- Désactiver l’autocommit pour tous les processus métier multi-requêtes afin de contrôler explicitement chaque validation.
- Encapsuler les requêtes SQL dans un TRY/CATCH (SQL Server) ou gérer les erreurs côté application pour garantir un
ROLLBACKsystématique. - Limiter la durée des transactions : une transaction longue pose des verrous qui bloquent les autres utilisateurs sur les tables modifiées.
- Vérifier @@TRANCOUNT avant un
COMMITen SQL Server pour éviter les validations prématurées dans les transactions imbriquées. - Tester les chemins d’erreur en préproduction en simulant des contraintes violées et des pannes réseau.
Erreurs courantes
Erreur 1 : oublier le ROLLBACK après une erreur
Une transaction ouverte sans ROLLBACK pose des verrous indéfinis sur les tables et bloque les autres utilisateurs.
-- ❌ Transaction laissée ouverte
BEGIN TRANSACTION;
UPDATE comptes SET solde = solde - 1000 WHERE id = 1;
-- Erreur détectée, mais pas de ROLLBACK → verrous actifs indéfiniment
-- ✅ Toujours fermer la transaction
BEGIN TRANSACTION;
UPDATE comptes SET solde = solde - 1000 WHERE id = 1;
ROLLBACK; -- Libère immédiatement les verrous
Erreur 2 : confondre COMMIT imbriqué et validation finale en SQL Server
En SQL Server, un COMMIT dans une transaction imbriquée décrémente @@TRANCOUNT mais ne valide pas les données tant que le compteur n’atteint pas zéro.
-- ❌ Penser que ce COMMIT valide définitivement les données
BEGIN TRANSACTION; -- @@TRANCOUNT = 1
BEGIN TRANSACTION; -- @@TRANCOUNT = 2
COMMIT; -- @@TRANCOUNT = 1 → pas encore validé !
-- ✅ Vérifier @@TRANCOUNT avant de considérer la transaction comme terminée
SELECT @@TRANCOUNT; -- Doit être 0 pour une validation définitive
Erreur 3 : utiliser SAVEPOINT sans ROLLBACK TO en cas d’erreur
Créer un SAVEPOINT sans revenir dessus en cas d’erreur ne sert à rien. Le COMMIT final valide toutes les opérations, y compris celles qui auraient dû être annulées.
-- ❌ SAVEPOINT créé mais jamais utilisé
BEGIN TRANSACTION;
UPDATE stocks SET quantite = quantite - 5 WHERE id = 10;
SAVEPOINT avant_vente;
INSERT INTO ventes (produit_id, quantite) VALUES (10, 5);
-- Erreur ignorée !
COMMIT; -- Tout est validé, y compris l'erreur
-- ✅ Revenir au SAVEPOINT en cas d'erreur détectée
ROLLBACK TO SAVEPOINT avant_vente;
COMMIT; -- Seule la mise à jour du stock est validée
Sources: MySql , Postgresql , MariaDb , SQL Server