La commande SQL DELETE supprime définitivement une ou plusieurs lignes d’une table en ciblant les enregistrements via une clause WHERE.
Table des matières
Syntaxe de base
La syntaxe DELETE repose sur deux éléments obligatoires : le nom de la table après le mot-clé FROM et une condition WHERE pour cibler les lignes. Sans WHERE, toutes les lignes de la table sont supprimées sans possibilité de récupération. Cette règle s’applique de manière identique sur MySQL, PostgreSQL, SQL Server et MariaDB.
DELETE FROM commandes WHERE condition;
La table commandes contient quatre enregistrements utilisés dans les exemples suivants.
| id | client_id | montant | date_commande | statut |
|---|---|---|---|---|
| 1 | 101 | 1250.00 | 2025-12-01 | payée |
| 2 | 102 | 45.00 | 2025-12-02 | en_attente |
| 3 | 101 | 8.50 | 2025-12-03 | annulée |
| 4 | 103 | 320.00 | 2025-12-04 | payée |
Supprimer une ligne via son identifiant
Cibler une ligne via son identifiant unique est la forme la plus sûre. Cette approche garantit qu’une seule ligne est supprimée, sans risque de toucher d’autres enregistrements. Elle convient aux suppressions manuelles déclenchées par une action utilisateur dans une interface d’administration.
La requête suivante supprime la commande annulée dont l’identifiant est 3.
DELETE FROM commandes WHERE id = 3;
| id | client_id | montant | date_commande | statut |
|---|---|---|---|---|
| 1 | 101 | 1250.00 | 2025-12-01 | payée |
| 2 | 102 | 45.00 | 2025-12-02 | en_attente |
| 4 | 103 | 320.00 | 2025-12-04 | payée |
La ligne dont l’id vaut 3 est supprimée, les trois autres restent intactes. Vérifiez que l’identifiant ciblé est bien unique dans la table avant d’exécuter la requête.
Supprimer avec une condition multiple
La condition WHERE accepte plusieurs conditions combinées avec AND et OR pour cibler un ensemble précis d’enregistrements. Cette forme supprime en une seule opération toutes les lignes correspondant aux critères définis. Par exemple, nettoyer les petites commandes et les commandes annulées se fait avec une condition OR.
La requête suivante supprime les commandes dont le montant est inférieur à 50 € ou dont le statut est annulée.
DELETE FROM commandes
WHERE montant < 50 OR statut = 'annulée';
Les lignes 2 et 3 sont supprimées car elles correspondent à au moins une des deux conditions.
| id | client_id | montant | date_commande | statut |
|---|---|---|---|---|
| 1 | 101 | 1250.00 | 2025-12-01 | payée |
| 4 | 103 | 320.00 | 2025-12-04 | payée |
Sélectionner les enregistrements avant de supprimer
Exécuter une sélection avec la même condition WHERE avant toute suppression affiche exactement les lignes qui seront supprimées. Cette étape préventive évite les suppressions accidentelles sur des données non prévues. L’appel à la fonction ROW_COUNT() confirme ensuite le nombre de lignes effectivement supprimées après l’opération.
La séquence suivante couvre les trois étapes d’une suppression sécurisée : comptage, visualisation, puis suppression.
-- Étape 1 : compter les lignes ciblées
SELECT COUNT(*) FROM commandes WHERE statut = 'annulée';
-- Étape 2 : visualiser les lignes concernées
SELECT * FROM commandes WHERE statut = 'annulée';
-- Étape 3 : supprimer après validation
DELETE FROM commandes WHERE statut = 'annulée';
-- Étape 4 : vérifier le nombre de lignes supprimées
SELECT ROW_COUNT();
ROW_COUNT() retourne le nombre exact de lignes supprimées par la dernière requête de suppression. Un résultat différent du comptage initial indique une incohérence à investiguer avant tout COMMIT.
Supprimer avec une transaction
Une transaction encapsule l’opération et permet de l’annuler intégralement si le résultat ne correspond pas à l’attendu. Sans transaction, les enregistrements supprimées sont perdues dès l’exécution, sans aucun retour en arrière possible. Cette protection est obligatoire dès que la suppression dépasse 100 lignes ou touche des données critiques.
La séquence suivante applique le protocole transactionnel complet sur la table archives.
START TRANSACTION;
-- Identifier les lignes ciblées
SELECT COUNT(*) FROM archives WHERE created_at < '2025-01-01';
-- Supprimer par lot de 100 lignes
DELETE FROM archives
WHERE created_at < '2025-01-01'
LIMIT 100;
-- Vérifier le résultat
SELECT COUNT(*) FROM archives WHERE created_at < '2025-01-01';
COMMIT; -- ou ROLLBACK si anomalie détectée
Le COMMIT valide définitivement la suppression uniquement après vérification du résultat, alors qu’un ROLLBACK restaure toutes les lignes supprimées depuis le début de la transaction si une anomalie est détectée.
Supprimer sur plusieurs tables avec une jointure
DELETE JOIN supprime simultanément des lignes liées entre deux tables via une clé commune. Cette technique évite les enregistrements orphelins qui subsistent quand une seule table est nettoyée. Par exemple, supprimer des commandes et leurs détails associés en une seule opération garantit la cohérence des données.
La syntaxe diffère entre MySQL et PostgreSQL pour les suppressions multi-tables.
-- MySQL : suppression simultanée dans deux tables
DELETE c, cd
FROM commandes c
JOIN commandes_details cd ON c.id = cd.commande_id
WHERE c.date_commande < '2025-01-01';
-- PostgreSQL : syntaxe avec USING
DELETE FROM commandes
USING commandes_details
WHERE commandes.id = commandes_details.commande_id
AND commandes.date_commande < '2025-01-01'
RETURNING commandes.id;
MySQL supprime les lignes dans les deux tables en une seule passe. PostgreSQL retourne les identifiants supprimés grâce à RETURNING, ce qui facilite l’audit de l’opération.
Supprimer avec EXISTS
DELETE WHERE NOT EXISTS supprime les lignes d’une table qui n’ont aucune correspondance dans une autre table. Cette logique corrélée vérifie l’existence de dépendances ligne par ligne avant chaque suppression. Elle convient aux nettoyages de clients inactifs ou d’enregistrements sans référence valide.
La requête suivante supprime les clients qui n’ont passé aucune commande depuis le 1er janvier 2025.
DELETE FROM clients c
WHERE NOT EXISTS (
SELECT 1 FROM commandes com
WHERE com.client_id = c.id
AND com.date_commande > '2025-01-01'
);
Un client dont l’identifiant apparaît dans commandes avec une date récente est conservé, tandis qu’un autre client sans aucune commande après la date limite est supprimé définitivement.
Implémenter le soft DELETE
Le soft DELETE marque une ligne comme supprimée via une colonne deleted_at au lieu de la détruire physiquement, ce qui permet de récupérer des données effacées par erreur et de maintenir un historique complet pour l’audit. Elle est recommandée pour les tables métier soumises aux obligations légales comme le RGPD.
La structure suivante ajoute les deux colonnes nécessaires au soft DELETE sur la table produits.
-- Ajouter les colonnes de soft DELETE
ALTER TABLE produits ADD COLUMN deleted_at TIMESTAMP NULL;
ALTER TABLE produits ADD COLUMN statut ENUM('actif', 'supprimé') DEFAULT 'actif';
-- Marquer comme supprimé au lieu de détruire
UPDATE produits
SET deleted_at = NOW(), statut = 'supprimé'
WHERE stock = 0 AND ventes = 0;
-- Requêtes courantes excluant les lignes supprimées
SELECT * FROM produits WHERE deleted_at IS NULL;
-- Récupérer une ligne supprimée par erreur
UPDATE produits SET deleted_at = NULL, statut = 'actif' WHERE id = 42;
Les requêtes courantes ajoutent WHERE deleted_at IS NULL pour ignorer les lignes marquées. La récupération d’une ligne supprimée par erreur se fait avec une simple mise à jour sans aucune perte de données.
Compatibilité SGBD
| Fonctionnalité | MySQL | PostgreSQL | SQL Server |
|---|---|---|---|
| Limite lignes | LIMIT 100 | Sous-requête | TOP(100) |
| Jointure multi-tables | DELETE t1, t2 FROM | DELETE USING | ❌ MERGE |
| Résultat supprimé | ROW_COUNT() | RETURNING * | OUTPUT |
| Transaction | ✅ | ✅ | ✅ |
| Soft DELETE | Manuel | Manuel | Manuel |
SQLite supporte DELETE FROM avec WHERE depuis la version 3.33. Testez toujours la syntaxe sur le SGBD cible avant de déployer en production.
Bonnes pratiques
Règle 1 — Toujours exécuter un SELECT avant le DELETESELECT COUNT(*), MIN(id), MAX(id) FROM commandes WHERE statut = 'annulée' valide le périmètre exact avant toute suppression irréversible.
Règle 2 — Encadrer dans une transaction expliciteSTART TRANSACTION suivi d’un ROLLBACK annule intégralement la suppression si le résultat post-DELETE révèle une anomalie.
Règle 3 — Limiter les volumes avec LIMITDELETE FROM logs WHERE date_log < '2025-01-01' LIMIT 5000 découpe l’opération en lots et évite le verrouillage prolongé de la table.
Règle 4 — Indexer la colonne utilisée dans WHERECREATE INDEX idx_statut ON commandes(statut) évite un parcours complet de la table et réduit le temps d’exécution sur les grandes tables.
Règle 5 — Préférer le soft DELETE pour les données métierUPDATE commandes SET deleted_at = NOW() est réversible et conforme aux obligations d’audit, contrairement au DELETE physique.
Cas pratique
Un service e-commerce purge chaque mois les commandes annulées de plus de 90 jours pour alléger la table principale, tout en conservant une trace dans la table d’audit.
START TRANSACTION;
SELECT COUNT(*) FROM commandes
WHERE statut = 'annulée'
AND date_commande < DATE_SUB(NOW(), INTERVAL 90 DAY);
DELETE FROM commandes
WHERE statut = 'annulée'
AND date_commande < DATE_SUB(NOW(), INTERVAL 90 DAY)
LIMIT 5000;
SET @supprimes = ROW_COUNT();
INSERT INTO audit_logs (action, table_cible, lignes, execute_le)
VALUES ('DELETE', 'commandes', @supprimes, NOW());
COMMIT;
Erreurs courantes
❌ Erreur : DELETE sans WHERE
DELETE FROM clients;
-- Toutes les lignes sont supprimées définitivement.
✅ Correct :
DELETE FROM clients WHERE derniere_connexion < '2025-01-01';
❌ Erreur : guillemets manquants sur une chaîne
DELETE FROM commandes WHERE statut = annulée;
-- Le moteur interprète annulée comme un nom de colonne inexistant.
✅ Correct :
DELETE FROM commandes WHERE statut = 'annulée';
❌ Erreur : fonction sur colonne indexée
DELETE FROM logs WHERE YEAR(date_log) = 2024;
-- La fonction YEAR() empêche l'utilisation de l'index sur date_log.
✅ Correct :
DELETE FROM logs WHERE date_log >= '2024-01-01' AND date_log < '2025-01-01';
Sources: Postgresql , MySql