Guide complet sur la suppression en cascade
L'article pilier – théorie et bonnes pratiques approfondies.
Comparatif complet des options ON DELETE CASCADE, RESTRICT, SET NULL et NO ACTION. Comment protéger l'intégrité de vos données tout en automatisant les suppressions.
Lorsqu'on définit une clé étrangère (FOREIGN KEY) dans une base de données relationnelle, il faut décider ce qui se passe quand la ligne parent est supprimée. Les concepteurs SQL ont prévu plusieurs comportements, car chaque situation métier est différente.
Que voulez-vous faire des commandes d'un client que vous supprimez ?
- Les supprimer aussi ? → CASCADE
- Empêcher la suppression du client ? → RESTRICT
- Les conserver mais sans lien ? → SET NULL

Tableau visuel des 4 comportements (assisté par Nano Banana 2)
| Option | Comportement | Données enfants après suppression |
|---|---|---|
| CASCADE | Supprime automatiquement les enfants | Supprimées |
| RESTRICT | Bloque la suppression du parent si des enfants existent | Conservées |
| NO ACTION | Similaire à RESTRICT (différence subtile de timing) | Conservées |
| SET NULL | Met la clé étrangère à NULL chez les enfants | Conservées mais sans lien |
| SET DEFAULT | Met une valeur par défaut chez les enfants | Conservées avec valeur par défaut |
CREATE TABLE commandes (
id INT PRIMARY KEY,
client_id INT,
FOREIGN KEY (client_id) REFERENCES clients(id)
ON DELETE CASCADE
);
Comportement : Supprimer un client → toutes ses commandes disparaissent.
Quand l'utiliser :
CREATE TABLE commandes (
id INT PRIMARY KEY,
client_id INT,
FOREIGN KEY (client_id) REFERENCES clients(id)
ON DELETE RESTRICT
);
Comportement : Supprimer un client qui a des commandes → erreur et blocage.
Quand l'utiliser :
CREATE TABLE commandes (
id INT PRIMARY KEY,
client_id INT, -- doit accepter NULL !
FOREIGN KEY (client_id) REFERENCES clients(id)
ON DELETE SET NULL
);
Comportement : Supprimer un client → ses commandes restent, mais client_id devient NULL.
Quand l'utiliser :

Illustration du comportement SET NULL (assisté par Nano Banana 2)
| Critère | CASCADE | RESTRICT |
|---|---|---|
| Objectif | Automatiser le nettoyage | Protéger les données enfants |
| Risque de perte de données | Élevé (suppression automatique) | Nul (bloque la suppression) |
| Travail manuel requis | Aucun | Supprimer les enfants d'abord |
| Intégrité référentielle | Maintient (tout supprime) | Maintient (bloque) |
| Historique conservé | Non | Oui (si on ne supprime pas) |
| Idéal pour | Données temporaires, logs | Données critiques, factures |
Non (ex : lignes de commande sans commande) → CASCADE
Oui (ex : commandes sans client anonymisé) → aller à la question 2
Oui → SET NULL (si colonne nullable) ou SET DEFAULT
Non → aller à la question 3
Oui, en plusieurs étapes → RESTRICT (bloque, oblige à supprimer les enfants d'abord)
Non, je préfère tout supprimer d'un coup → CASCADE
➔ CASCADE = je veux tout supprimer automatiquement
➔ SET NULL = je veux conserver l'historique sans lien
➔ RESTRICT = je veux être sûr de ne rien perdre accidentellement
-- Conservation obligatoire des commandes (facturation)
ALTER TABLE commandes
ADD CONSTRAINT fk_commandes_client
FOREIGN KEY (client_id) REFERENCES clients(id)
ON DELETE SET NULL; -- ou RESTRICT selon politique RGPD
-- Les logs sans utilisateur n'ont pas d'intérêt
ALTER TABLE logs
ADD CONSTRAINT fk_logs_user
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE;
-- Obligation légale de conservation (interdiction de supprimer)
ALTER TABLE factures
ADD CONSTRAINT fk_factures_client
FOREIGN KEY (client_id) REFERENCES clients(id)
ON DELETE RESTRICT;
RESTRICT et NO ACTION sont équivalents.SET DEFAULT est accepté syntaxiquement mais souvent ignoré.NO ACTION diffère de RESTRICT : vérification en fin de transaction.SET DEFAULT fonctionne si la colonne a une valeur par défaut.RESTRICT n'existe pas → utiliser NO ACTION.| Option | MySQL | PostgreSQL | SQL Server |
|---|---|---|---|
| CASCADE | ✅ | ✅ | ✅ |
| RESTRICT | ✅ | ✅ | ❌ (NO ACTION) |
| SET NULL | ✅ | ✅ | ✅ |
| SET DEFAULT | ⚠️ | ✅ | ✅ |
| NO ACTION | ✅ | ✅ | ✅ |
RESTRICT : la vérification est immédiate. La suppression est bloquée dès qu'on essaie.
NO ACTION : la vérification est différée en fin de transaction. Si d'autres modifications dans la transaction suppriment les enfants, NO ACTION peut réussir là où RESTRICT bloquerait.
En pratique, MySQL les traite de façon identique. PostgreSQL fait la différence.
Oui, en supprimant puis recréant la contrainte :ALTER TABLE enfants DROP CONSTRAINT fk_nom;ALTER TABLE enfants ADD CONSTRAINT fk_nom FOREIGN KEY (parent_id) REFERENCES parents(id) ON DELETE CASCADE;
Attention : cette opération peut être longue sur de grandes tables.
CASCADE peut être plus lent sur des suppressions massives car il supprime automatiquement toutes les lignes enfants (et leurs index). RESTRICT est plus rapide car il se contente de vérifier l'existence d'enfants (généralement via un index rapide). Pour des millions de lignes, la différence peut être significative.
Utilisez RESTRICT par défaut (protection), et écrivez des procédures stockées ou scripts qui suppriment explicitement les enfants avant le parent quand vous voulez un comportement CASCADE. Cela vous donne le contrôle total sans risque de suppression accidentelle massive.
Non. La colonne enfant doit accepter NULL (définie sans NOT NULL). Si la colonne a NOT NULL, SET NULL provoquera une erreur. Dans ce cas, utilisez SET DEFAULT (avec une valeur par défaut valide) ou RESTRICT/CASCADE.
Le choix entre CASCADE, RESTRICT, SET NULL ou SET DEFAULT n'est pas anodin. Il reflète votre stratégie de gestion des données et doit être aligné avec vos besoins métier.