SQL : INSERT, UPDATE, DELETE

Ajouter, modifier et supprimer des données — syntaxe, variantes, transactions et bonnes pratiques

10
Sections
25+
Exemples
DML
Standard

SECTION 01

DDL vs DML — deux familles de commandes

SQL est divisé en plusieurs sous-langages. Les deux principaux sont le DDL (Data Definition Language) et le DML (Data Manipulation Language). Le DDL concerne la structure de la base : créer, modifier ou supprimer des tables avec CREATE TABLE, ALTER TABLE et DROP TABLE. Le DML, lui, concerne les données à l’intérieur des tables. C’est le sujet de cette fiche.

Catégorie Commandes Agit sur
DDL (structure) CREATE, ALTER, DROP, TRUNCATE Tables, colonnes, index
DML (données) INSERT, UPDATE, DELETE, SELECT Lignes dans les tables

Les commandes DML sont réversibles avec un ROLLBACK (si tu es dans une transaction). Les commandes DDL sont généralement auto-committées — pas de retour en arrière possible. Cette distinction est essentielle quand tu travailles sur des données de production.

SECTION 02

INSERT — insérer une ligne

➕ Syntaxe de base

La commande INSERT INTO ajoute une ou plusieurs nouvelles lignes dans une table. Tu spécifies les colonnes cibles puis les valeurs correspondantes. Les colonnes avec un DEFAULT ou AUTO_INCREMENT peuvent être omises — le SGBD les remplit automatiquement.

— INSERT avec toutes les colonnes nommées (recommandé)
INSERT INTO clients (nom, email, ville)
VALUES (‘Alice’, ‘alice@email.com’, ‘Paris’);

— INSERT sans nommer les colonnes (déconseillé — fragile)
INSERT INTO clients
VALUES (NULL, ‘Bob’, ‘bob@email.com’, ‘Lyon’, NOW());
— ⚠️ Tu dois fournir une valeur pour CHAQUE colonne, dans le bon ordre
— Si l’ordre des colonnes change, la requête casse silencieusement

Bonne pratique : nomme toujours les colonnes dans ton INSERT. Le code est plus lisible, plus robuste, et il ne casse pas si quelqu’un ajoute une colonne à la table plus tard.

📦 Insérer plusieurs lignes en une fois

Au lieu de faire N requêtes INSERT séparées, tu peux insérer plusieurs lignes en une seule commande. C’est beaucoup plus performant car le SGBD n’exécute qu’un seul aller-retour réseau et un seul commit.

— INSERT multi-lignes (beaucoup plus rapide que des INSERT séparés)
INSERT INTO clients (nom, email, ville) VALUES
(‘Alice’, ‘alice@email.com’, ‘Paris’),
(‘Bob’, ‘bob@email.com’, ‘Lyon’),
(‘Charlie’, ‘charlie@email.com’, ‘Marseille’);
— 3 lignes insérées en une seule requête

SECTION 03

INSERT — variantes avancées

📥 INSERT … SELECT — insérer depuis une autre table

Cette variante est extrêmement utile pour dupliquer des données, archiver, ou remplir une table à partir d’une requête. Le SELECT remplace la clause VALUES — chaque ligne retournée par le SELECT est insérée dans la table cible.

— Archiver les commandes de 2024
INSERT INTO commandes_archive (id, client_id, montant, date)
SELECT id, client_id, montant, date
FROM commandes
WHERE date < ‘2025-01-01’;

— Copier des données entre tables
INSERT INTO newsletter_inscrits (email)
SELECT DISTINCT email FROM clients
WHERE opt_in = TRUE;

🔄 INSERT … ON DUPLICATE KEY / ON CONFLICT (UPSERT)

Un « upsert » est une opération qui insère une ligne si elle n’existe pas, et la met à jour si elle existe déjà (basé sur une contrainte UNIQUE ou PRIMARY KEY). C’est un pattern très courant pour les imports de données, les compteurs, et la synchronisation.

— MySQL — ON DUPLICATE KEY UPDATE
INSERT INTO compteurs (page_url, visites)
VALUES (‘/accueil’, 1)
ON DUPLICATE KEY UPDATE visites = visites + 1;

— PostgreSQL — ON CONFLICT
INSERT INTO compteurs (page_url, visites)
VALUES (‘/accueil’, 1)
ON CONFLICT (page_url) DO UPDATE SET
visites = compteurs.visites + 1;

— PostgreSQL — ignorer si doublon
INSERT INTO clients (email, nom)
VALUES (‘alice@email.com’, ‘Alice’)
ON CONFLICT (email) DO NOTHING;

L’upsert évite de faire un SELECT pour vérifier l’existence, puis un INSERT ou UPDATE séparé. C’est atomique (pas de race condition) et plus rapide. En MySQL, la contrainte peut être une PRIMARY KEY ou un index UNIQUE.

SECTION 04

UPDATE — modifier des données

✏️ Syntaxe de base

UPDATE modifie les valeurs de colonnes existantes. La clause WHERE détermine quelles lignes sont affectées. C’est la commande DML la plus dangereuse car un UPDATE sans WHERE modifie TOUTES les lignes de la table — une erreur très courante et souvent catastrophique en production.

— Modifier une ligne spécifique
UPDATE clients
SET ville = ‘Lyon’
WHERE id = 1;

— Modifier plusieurs colonnes en même temps
UPDATE clients
SET nom = ‘Alice Martin’,
email = ‘alice.martin@email.com’,
ville = ‘Bordeaux’
WHERE id = 1;

— Modifier plusieurs lignes avec une condition
UPDATE produits
SET prix = prix * 1.10 — augmentation de 10%
WHERE categorie = ‘Tech’;

— Utiliser des fonctions dans SET
UPDATE clients
SET email = LOWER(email),
updated_at = NOW()
WHERE email != LOWER(email); — seulement ceux qui ne sont pas déjà en minuscule

⚠️ TOUJOURS vérifier ton WHERE avant d’exécuter un UPDATE. Astuce : écris d’abord un SELECT avec le même WHERE pour voir quelles lignes seront affectées, puis remplace SELECT par UPDATE SET.

SECTION 05

UPDATE avancé

🔗 UPDATE avec JOIN

Parfois tu as besoin de mettre à jour une table en fonction de données dans une autre table. L’UPDATE avec JOIN permet de croiser les tables directement dans la modification. La syntaxe varie selon le SGBD.

— MySQL — UPDATE avec JOIN
UPDATE commandes co
INNER JOIN clients c ON co.client_id = c.id
SET co.ville_client = c.ville
WHERE co.ville_client IS NULL;

— PostgreSQL — UPDATE … FROM
UPDATE commandes co
SET ville_client = c.ville
FROM clients c
WHERE co.client_id = c.id
AND co.ville_client IS NULL;

📊 UPDATE avec sous-requête

Quand tu ne peux pas utiliser un JOIN directement (ou pour une meilleure portabilité SQL standard), la sous-requête est une alternative universelle :

— Mettre à jour le total de chaque client
UPDATE clients
SET total_depense = (
SELECT COALESCE(SUM(montant), 0)
FROM commandes
WHERE commandes.client_id = clients.id
);

— Augmenter le prix des produits les plus vendus
UPDATE produits
SET prix = prix * 1.05
WHERE id IN (
SELECT produit_id FROM commandes
GROUP BY produit_id
HAVING COUNT(*) > 50
);

SECTION 06

DELETE — supprimer des données

🗑️ Syntaxe de base

DELETE supprime des lignes d’une table. Comme UPDATE, la clause WHERE est cruciale — un DELETE sans WHERE supprime toutes les lignes de la table. La suppression est réversible si tu es dans une transaction (ROLLBACK), mais irréversible après un COMMIT.

— Supprimer une ligne par son ID
DELETE FROM clients WHERE id = 42;

— Supprimer avec une condition
DELETE FROM sessions
WHERE derniere_activite < NOW() INTERVAL 30 DAY;

— Supprimer les doublons (garder l’ID le plus petit)
DELETE c1 FROM clients c1
INNER JOIN clients c2
ON c1.email = c2.email
AND c1.id > c2.id; — MySQL

— Supprimer avec sous-requête
DELETE FROM commandes
WHERE client_id NOT IN (
SELECT id FROM clients
);

⚠️ Même réflexe qu’avec UPDATE : écris d’abord un SELECT avec le même WHERE pour vérifier les lignes qui seront supprimées. Puis remplace SELECT par DELETE.

🛡️ Soft delete — marquer au lieu de supprimer

En production, beaucoup d’applications ne suppriment jamais réellement les données. À la place, elles utilisent un « soft delete » : une colonne deleted_at qui contient la date de suppression. Les requêtes ajoutent un WHERE deleted_at IS NULL pour ignorer les lignes « supprimées ». Cela permet de récupérer des données accidentellement supprimées et de maintenir l’intégrité référentielle.

— Soft delete : marquer comme supprimé
UPDATE clients
SET deleted_at = NOW()
WHERE id = 42;

— Lire seulement les clients « actifs »
SELECT * FROM clients WHERE deleted_at IS NULL;

— Restaurer un client supprimé
UPDATE clients SET deleted_at = NULL WHERE id = 42;

SECTION 07

TRUNCATE vs DELETE

Les deux suppriment des données, mais de manière fondamentalement différente. DELETE supprime ligne par ligne, déclenche les triggers, respecte les FOREIGN KEY, et peut être annulé dans une transaction. TRUNCATE vide la table d’un coup en réinitialisant le stockage — c’est beaucoup plus rapide mais irréversible.

Critère DELETE FROM table TRUNCATE TABLE table
Vitesse ⚠️ Lent (ligne par ligne) ✅ Très rapide (reset complet)
WHERE ✅ Oui (suppression partielle) ❌ Non (vide TOUTE la table)
ROLLBACK ✅ Possible (dans une transaction) ❌ Non (DDL, auto-commit)
Triggers ✅ Déclenchés ❌ Non déclenchés
AUTO_INCREMENT Continue (ex : prochain id = 1001) Reset à 1
FOREIGN KEY ✅ Vérifie les contraintes ❌ Échoue si FK référencée
Type DML (données) DDL (structure)
— Vider complètement une table (rapide, irréversible)
TRUNCATE TABLE logs;

— Supprimer toutes les lignes (lent, réversible)
DELETE FROM logs;

SECTION 08

Transactions

🔒 Tout ou rien

Une transaction regroupe plusieurs opérations en un bloc atomique. Soit tout réussit (COMMIT), soit tout est annulé (ROLLBACK). C’est indispensable quand plusieurs modifications doivent rester cohérentes — par exemple, transférer de l’argent entre deux comptes. Si le débit réussit mais le crédit échoue, les deux doivent être annulés.

— Transférer 100 € du compte A au compte B
START TRANSACTION; — MySQL (BEGIN en PostgreSQL)

UPDATE comptes SET solde = solde 100 WHERE id = 1; — débit
UPDATE comptes SET solde = solde + 100 WHERE id = 2; — crédit

— Si tout va bien :
COMMIT;

— Si erreur :
ROLLBACK; — annule les deux UPDATE

Bonne pratique en production : encadre toujours tes DELETE et UPDATE critiques dans une transaction. Si tu vois que le nombre de lignes affectées est anormal, ROLLBACK avant le COMMIT.

SECTION 09

Erreurs fréquentes

Erreur Conséquence Solution
UPDATE / DELETE sans WHERE Modifie / supprime TOUTES les lignes Toujours écrire le SELECT d’abord pour vérifier
INSERT sans nommer les colonnes Casse si l’ordre des colonnes change Toujours INSERT INTO table (col1, col2)
Violation de FOREIGN KEY Erreur à l’INSERT ou DELETE Vérifier les dépendances, utiliser ON DELETE CASCADE
Violation de UNIQUE Erreur à l’INSERT Utiliser ON CONFLICT / ON DUPLICATE KEY
Pas de transaction pour des opérations liées Incohérence si crash au milieu BEGIN / COMMIT / ROLLBACK
TRUNCATE sur une table avec FK Erreur si d’autres tables référencent Utiliser DELETE ou supprimer les FK d’abord

SECTION 10

Questions fréquentes

Comment savoir combien de lignes ont été affectées ?
Après un INSERT, UPDATE ou DELETE, le SGBD retourne le nombre de lignes affectées. En MySQL : ROW_COUNT(). En PostgreSQL : le message UPDATE 5 ou DELETE 3. Dans le code applicatif (PHP, Python, Node.js), la fonction d’exécution retourne ce nombre — toujours le vérifier avant de COMMIT.
INSERT … RETURNING — c’est quoi ?
PostgreSQL permet d’ajouter RETURNING * (ou RETURNING id, nom) à un INSERT, UPDATE ou DELETE pour récupérer les lignes affectées dans le même aller-retour. Très pratique pour récupérer l’ID auto-généré : INSERT INTO clients (nom) VALUES (‘Alice’) RETURNING id. MySQL utilise LAST_INSERT_ID() à la place.
Peut-on annuler un TRUNCATE ?
Non dans la plupart des SGBD. TRUNCATE est une commande DDL qui est auto-committée. En PostgreSQL cependant, TRUNCATE est transactionnel et peut être ROLLBACK. C’est une exception — ne compte pas dessus avec MySQL ou SQL Server.
DELETE ou soft delete ?
Le soft delete (colonne deleted_at) est préféré en production pour les données importantes : clients, commandes, comptes. Il permet la récupération et l’audit. Le vrai DELETE est approprié pour les données temporaires comme les sessions, les logs, les tokens expirés. Choisis en fonction de la criticité des données.

INSERT, UPDATE, DELETE en SQL — Manipuler les données

Référence : sql.sh INSERT INTO