SQL BEGIN TRANSACTION

Par :

,le

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.

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.

SGBDSyntaxe
MySQL / MariaDBSTART TRANSACTION ou BEGIN
PostgreSQLBEGIN ou START TRANSACTION
SQL ServerBEGIN 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.

NiveauLectures salesLectures non répétablesLectures 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éfinitionExemple concret
AtomicitéTout ou rien : toutes les opérations réussissent ou aucune n’est appliquéeUn virement annulé ne débite ni ne crédite aucun compte
CohérenceLa transaction fait passer la base d’un état valide à un autre état valideUne contrainte de clé étrangère reste respectée après chaque transaction
IsolationChaque transaction s’exécute sans voir les modifications non validées des autresDeux virements simultanés ne se lisent pas mutuellement avant COMMIT
DurabilitéUne fois validée, la transaction survit aux pannes et redémarragesUn 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èreACID (SQL)BASE (NoSQL distribué)
CohérenceImmédiate et absolueProgressive
DisponibilitéConditionnelle à la stabilitéPrioritaire
Cas d’usageBanque, ERP, e-commerceRé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 ROLLBACK systé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 COMMIT en 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