TRUNCATE — la suppression rapide de toutes les lignes et le High Water Mark

Un guide illustré de la différence entre DELETE et TRUNCATE : comment fonctionne le High Water Mark (HWM), si tu peux faire un rollback après exécution, la vraie raison de l'écart de vitesse, et comment choisir entre les deux sur Oracle / PostgreSQL / MySQL.

Le code de cet article cible les SGBDR de production

La syntaxe TRUNCATE TABLE de cet article est la forme utilisée par les SGBDR de production comme MySQL / PostgreSQL / Oracle / SQL Server. La console navigateur de ce cours (qui utilise SQLite) n'accepte pas directement le mot-clé TRUNCATE. Sous SQLite, DELETE FROM table; produit le même effet, et une optimisation truncate interne lui donne la même vitesse que TRUNCATE.

TRUNCATE — vider toutes les lignes, vite

Il y a deux manières de supprimer toutes les lignes d'une table d'un coup. La première est DELETE FROM table; (sans WHERE), couverte dans l'article précédent. La seconde est `TRUNCATE TABLE table;`, le sujet de cet article. Les deux laissent la table vide, mais la façon dont elles y arrivent et leur vitesse sont complètement différentes.

DELETE est une commande DML (Data Manipulation Language) qui retire les lignes une par une, en laissant une entrée de log pour chaque suppression. TRUNCATE, à l'inverse, penche vers le DDL (Data Definition Language) — il réinitialise toute la table sans tenue de comptes ligne par ligne, presque comme si la définition même de la table était recréée à partir de zéro.

En quoi DELETE et TRUNCATE diffèrent
DELETE FROM table;TRUNCATE TABLE table;Retire les lignes une par une(restreignable avec WHERE)Réinitialise la table d'un coup(WHERE non autorisé)Traité comme DMLPeut être rollbackéTraité comme DDL (selon la base)Peut commiter à l'exécutionLog de suppression par ligneDéclenche les triggersLog minimalIgnore généralement les triggersLent sur les très grandes tablesInstantané mêmesur les très grandes tables
DELETE est une commande DML qui retire les lignes une par une et conserve un log ; TRUNCATE penche vers le DDL et réinitialise la table entière d'un coup. Ils diffèrent sur le support de WHERE, le rollback après exécution et la vitesse.
-- 1) DELETE de tout (log de suppression par ligne, peut être rollbacké)
DELETE FROM customer;

-- 2) TRUNCATE de tout (reset en une fois, pas de log par ligne, commit immédiat en général)
TRUNCATE TABLE customer;

-- Note : la console de ce cours (SQLite) n'accepte pas TRUNCATE.
--   DELETE FROM customer; bénéficie en interne de la même optimisation truncate et reste rapide.

Différences entre MySQL / PostgreSQL / Oracle

La syntaxe TRUNCATE TABLE table; elle-même est partagée par MySQL / PostgreSQL / Oracle / SQL Server, mais le comportement fin diffère selon la base.

- MySQL : traité comme DDL, ne peut pas être rollbacké, AUTO_INCREMENT est réinitialisé

- PostgreSQL : peut être rollbacké, RESTART IDENTITY réinitialise aussi la séquence

- Oracle : traité comme DDL, ne peut pas être rollbacké, REUSE STORAGE / DROP STORAGE permet de contrôler comment le stockage est rendu

- SQL Server : peut être rollbacké

Le High Water Mark — ce qui se cache derrière l'écart de vitesse

La clé pour comprendre l'écart de vitesse entre DELETE et TRUNCATE est le concept de High Water Mark (HWM). Le HWM est un marqueur qui pointe vers le bloc de stockage le plus haut que la table ait jamais utilisé. Le terme vient d'Oracle, mais PostgreSQL / SQL Server / MySQL (InnoDB) ont tous des optimisations équivalentes qui fonctionnent en interne.

Quand la base fait un full scan (un balayage ligne par ligne comme SELECT * FROM table), elle lit chaque bloc depuis le début de la table jusqu'au HWM dans l'ordre. Plus le HWM est haut, plus elle a de blocs à lire — et comme elle ne saute pas les blocs vides, un scan peut être lent même sur une table vide si le HWM est haut.

Comment fonctionne le High Water Mark (HWM)
Cible du scan = blocs au niveau ou en dessous du HWM── HWM (bloc 5) ──Position la plus hauteque la table ait utiliséebloc 5 : ●dataLe full scan littout en dessousbloc 4 : ●databloc 3 : ●databloc 2 : ●databloc 1 : ●dataDébut de la table(bloc 0)
À l'intérieur de la table se trouve une séquence de blocs de taille fixe. Le HWM marque « le bloc le plus haut jamais utilisé ». Un full scan lit chaque bloc situé au niveau du HWM ou en dessous.

Le comportement crucial ici, c'est que DELETE n'abaisse pas le HWM. Quand tu lances DELETE FROM table; pour retirer toutes les lignes, le contenu de la table devient vide, mais les blocs eux-mêmes restent dans le segment. Le HWM reste sur bloc 5. Lance ensuite SELECT COUNT(*) FROM table; et même si le compte de lignes vaut 0, la base lit chaque bloc au niveau du HWM ou en dessous jusqu'au bout — en prenant à peu près autant de temps qu'avant la suppression.

TRUNCATE TABLE table; est la commande qui réinitialise le HWM au début de la table, ramenant instantanément la cible du full scan à 0 bloc. C'est la vraie raison pour laquelle TRUNCATE est décrit comme « instantané même sur les très grandes tables ».

HWM après DELETE vs après TRUNCATE
ÉtatInitial (5 lignes)DELETE FROMtable;TRUNCATE TABLEtable;Compte de lignes5 lignes0 ligne0 ligneBlocs5 utilisés5 (vides)RéinitialisésHWMbloc 5toujoursbloc 5remis àbloc 0Full scan5 blocs5 blocs(vides inclus)0 bloc(instantané)
Après les deux, le compte de lignes vaut 0. Mais la position du HWM est différente, ce qui fait une énorme différence sur la vitesse des full scans suivants.

DELETE n'a pas réduit la taille des données ?

Je suis tombé sur une situation en production où j'avais lancé DELETE pour réduire la taille des données, mais l'usage du stockage avait à peine baissé.

La cause était exactement celle-ci : le HWM ne descend pas. DELETE ne retire les lignes que logiquement, alors que les blocs (pages) que la table avait réservés restent en place. Le fichier sur disque conserve toujours cette zone, donc du point de vue de l'OS, la taille des données n'a pas changé.

Quand tu veux vraiment réduire le stockage, lance la commande de réorganisation propre à chaque base (VACUUM FULL sur PostgreSQL, OPTIMIZE TABLE sur MySQL InnoDB, ALTER TABLE ... SHRINK SPACE sur Oracle), ou utilise TRUNCATE pour un nettoyage complet.

Choisir entre DELETE et TRUNCATE

En pratique, le choix n'est pas simplement « TRUNCATE pour tout, DELETE quand il faut une condition ». Les vrais facteurs sont si tu as besoin de pouvoir rollbacker, si les triggers doivent se déclencher, et à quel point la vitesse compte. Le tableau de comparaison ci-dessous est la checklist standard.

CritèreDELETETRUNCATE
GranularitéPar ligneTable entière
Clause WHEREAutoriséeNon autorisée (toujours toutes les lignes)
Vitesse (très grandes tables)Lent (traitement par ligne)Rapide (instantané)
Rollback après exécutionPossibleSelon la base (Oracle / MySQL : non)
Déclenchement de triggersSe déclenchentEn général ne se déclenchent pas
AUTO_INCREMENTPréservéRéinitialisé (selon la base)
High Water MarkNe bouge pasRéinitialisé
Log de suppressionEntrée par ligne (volumineux)Minimal
ClassificationDMLDDL (selon la base)

Pièges de TRUNCATE

TRUNCATE est rapide et pratique, mais c'est typiquement une opération qu'on ne peut pas reprendre. Fais attention à :

- Certaines bases commitent à l'exécution (Oracle / MySQL). En production, lance d'abord SELECT COUNT(*) pour confirmer le compte de lignes avant de tirer

- `AUTO_INCREMENT` est réinitialisé (selon la base). Si les ids sont exposés à des systèmes externes ou à des URL, attention aux collisions après reset

- Les contraintes de clé étrangère peuvent le bloquer (la plupart des bases). Si subscription référence customer, tu devras d'abord vider la table fille pour faire un TRUNCATE de customer

- Les triggers ne se déclenchent généralement pas. Un trigger est le mécanisme de la base qui exécute automatiquement du SQL en réponse à des insertions / mises à jour / suppressions de lignes — par exemple « écrire une entrée d'audit dans une autre table chaque fois qu'une ligne est supprimée ». DELETE déclenche ce trigger ligne par ligne, mais TRUNCATE n'opère pas au niveau ligne, donc les logs d'audit et autres enregistrements similaires ne seront pas créés

Sors TRUNCATE quand tu as besoin de la vitesse maximale pour un nettoyage massif de très grosses données ; sinon DELETE WHERE ou un DELETE rollbackable est le choix plus sûr.

QUIZ

Vérification des connaissances

Répondez à chaque question une par une.

Question 1Laquelle des descriptions de DELETE et TRUNCATE est correcte ?

Question 2Quelle est la meilleure description du High Water Mark (HWM) ?

Question 3Juste après avoir vidé une table d'un million de lignes avec DELETE FROM table; (sans WHERE), tu lances SELECT COUNT(*) FROM table;. Quel comportement est correct ?