🧚 Écoute ! Beekeeper Studio est une interface de base de données rapide, moderne et open source Télécharger
7 janvier 2026 Par Matthew Rathbone

Supprimer les doublons en SQL peut vouloir dire deux choses : masquer les lignes répétées dans le résultat d’une requête avec DISTINCT, ou supprimer les copies en trop dans la table pour n’en garder qu’une de chaque. Les exemples fonctionnent sous PostgreSQL, MySQL, SQLite et SQL Server.

Comment supprimer les doublons en SQL

SELECT DISTINCT renvoie chaque combinaison des colonnes listées une seule fois, sans toucher à la table. Pour nettoyer la table elle-même, supprime toutes les lignes sauf une dans chaque groupe de doublons, en général celle qui a l’id le plus petit :

SELECT DISTINCT column_list FROM table_name;

DELETE FROM table_name
WHERE id NOT IN (
  SELECT id FROM (
    SELECT MIN(id) AS id FROM table_name GROUP BY column_list
  ) AS keepers
);

Ici, Maria Lopez a été saisie trois fois :

-- Input: customers
-- id | name        | email             | phone
-- ---+-------------+-------------------+---------
-- 1  | Maria Lopez | maria@example.com | 555-0142
-- 2  | Sam Patel   | sam@example.com   | 555-0178
-- 3  | Maria Lopez | maria@example.com | 555-0142
-- 4  | Lena Novak  | lena@example.com  | 555-0121
-- 5  | Maria Lopez | maria@example.com | 555-0142

SELECT DISTINCT name, email, phone
FROM customers
ORDER BY name;

-- Output:
-- name        | email             | phone
-- ------------+-------------------+---------
-- Lena Novak  | lena@example.com  | 555-0121
-- Maria Lopez | maria@example.com | 555-0142
-- Sam Patel   | sam@example.com   | 555-0178

DELETE FROM customers
WHERE id NOT IN (
  SELECT id FROM (
    SELECT MIN(id) AS id FROM customers GROUP BY name, email, phone
  ) AS keepers
);

-- Output (customers after the DELETE):
-- id | name        | email             | phone
-- ---+-------------+-------------------+---------
-- 1  | Maria Lopez | maria@example.com | 555-0142
-- 2  | Sam Patel   | sam@example.com   | 555-0178
-- 4  | Lena Novak  | lena@example.com  | 555-0121

Les règles à connaître

  • Liste les colonnes qui font de deux lignes des doublons. Laisse de côté l’id et tout ce qui est toujours différent, comme un horodatage created_at. Les lignes qui diffèrent sur une des colonnes listées sont toutes conservées.
  • L’enveloppe SELECT id FROM (...) AS keepers sert pour MySQL, qui sinon refuse qu’un DELETE lise sa propre table dans une sous-requête : « You can’t specify target table ‘customers’ for update in FROM clause ».
  • Regarde avant de supprimer. Lance d’abord la requête GROUP BY ou ROW_NUMBER() pour voir ce qui va disparaître, et fais la suppression dans une transaction que tu peux annuler.
  • Bloque les nouveaux doublons avec un index unique une fois la table propre :
CREATE UNIQUE INDEX customers_email_key ON customers (email);

INSERT INTO customers VALUES (6, 'Maria Lopez', 'maria@example.com', '555-0142');
-- ERROR: duplicate key value violates unique constraint "customers_email_key"

Syntaxe alternative

Dans du code SQL Server, tu verras souvent les copies numérotées dans une CTE puis supprimées à travers elle, ce que seul SQL Server permet. Sur la table customers d’origine :

-- SQL Server
WITH numbered AS (
  SELECT *, ROW_NUMBER() OVER (
    PARTITION BY name, email, phone ORDER BY id
  ) AS row_num
  FROM customers
)
DELETE FROM numbered WHERE row_num > 1;

-- Output (customers after the DELETE):
-- id | name        | email             | phone
-- ---+-------------+-------------------+---------
-- 1  | Maria Lopez | maria@example.com | 555-0142
-- 2  | Sam Patel   | sam@example.com   | 555-0178
-- 4  | Lena Novak  | lena@example.com  | 555-0121

Pour combiner deux requêtes sans répéter de lignes, utilise UNION, qui supprime les doublons, plutôt que UNION ALL, qui les garde :

SELECT 'apple' AS fruit UNION ALL SELECT 'apple';

-- Output:
-- fruit
-- -----
-- apple
-- apple

SELECT 'apple' AS fruit UNION SELECT 'apple';
-- Output: apple

Quand faut-il supprimer les doublons ?

Parfois, il suffit de masquer les répétitions dans un résultat, et parfois c’est la table elle-même qu’il faut nettoyer. Voici les étapes habituelles, de l’inspection à la suppression.

Afficher chaque valeur une seule fois

Pour un rapport, une liste déroulante ou un coup d’œil rapide au contenu d’une colonne, SELECT DISTINCT retire les répétitions du résultat et laisse la table telle quelle.

Compter les copies avant de supprimer

Groupe par les colonnes qui définissent un doublon et garde les groupes avec HAVING COUNT(*) > 1. Tu vois ainsi ce qui est en double et combien de fois, avant de supprimer quoi que ce soit. Le guide pour trouver les doublons en SQL présente d’autres méthodes.

Choisir la copie à garder

ROW_NUMBER() numérote les lignes dans chaque groupe de doublons. L’ORDER BY dans OVER (...) décide quelle ligne reçoit le 1 et reste : la plus ancienne, la plus récente ou la plus complète.

Supprimer les copies en trop

Une fois que tu sais quelle ligne garder, supprime les autres. Pour garder la plus récente au lieu de la plus ancienne, numérote les lignes par date, de la plus récente à la plus ancienne, et supprime tout ce qui a un numéro supérieur à 1.

Exemples concrets de suppression de doublons

Tous les pays et transporteurs avec lesquels tu expédies

DISTINCT s’applique à la ligne entière des colonnes sélectionnées, donc le Canada apparaît une fois par transporteur :

-- Input: shipments
-- order_id | country | carrier
-- ---------+---------+--------
-- 1001     | Canada  | UPS
-- 1002     | Germany | DHL
-- 1003     | Canada  | FedEx
-- 1004     | Canada  | UPS
-- 1005     | Germany | DHL

SELECT DISTINCT country, carrier
FROM shipments
ORDER BY country, carrier;

-- Output:
-- country | carrier
-- --------+--------
-- Canada  | FedEx
-- Canada  | UPS
-- Germany | DHL

Des adresses e-mail qui ne diffèrent que par la casse

Groupe par l’e-mail en minuscules pour que Jon@Example.com et jon@example.com comptent comme des copies. (MySQL et SQL Server ignorent déjà la casse par défaut, et LOWER() n’y fait aucun mal.)

-- Input: accounts
-- id | email
-- ---+----------------------
-- 1  | Ana.Silva@example.com
-- 2  | jon@example.com
-- 3  | ana.silva@example.com
-- 4  | Jon@Example.com
-- 5  | mei@example.com

SELECT LOWER(email) AS email, COUNT(*) AS copies
FROM accounts
GROUP BY LOWER(email)
HAVING COUNT(*) > 1
ORDER BY email;

-- Output:
-- email                 | copies
-- ----------------------+-------
-- ana.silva@example.com | 2
-- jon@example.com       | 2

Garder la copie qui a un numéro de téléphone

Trie d’abord les lignes qui ont un téléphone, puis par id, pour que la ligne la plus complète de chaque groupe porte le numéro 1 :

-- Input: contacts
-- id | email            | phone
-- ---+------------------+---------
-- 1  | ravi@example.com | NULL
-- 2  | ravi@example.com | 555-0110
-- 3  | ines@example.com | 555-0133
-- 4  | ines@example.com | NULL

SELECT
  id,
  email,
  phone,
  ROW_NUMBER() OVER (
    PARTITION BY email
    ORDER BY CASE WHEN phone IS NULL THEN 1 ELSE 0 END, id
  ) AS row_num
FROM contacts
ORDER BY email, row_num;

-- Output:
-- id | email            | phone    | row_num
-- ---+------------------+----------+--------
-- 3  | ines@example.com | 555-0133 | 1
-- 4  | ines@example.com | NULL     | 2
-- 2  | ravi@example.com | 555-0110 | 1
-- 1  | ravi@example.com | NULL     | 2

Supprimer les lignes numérotées 2 ou plus (ids 1 et 4) laisse un seul contact complet par e-mail.

Supprimer des copies exactes dans une table sans id

Quand les copies sont identiques sur toutes les colonnes, il n’y a pas d’id dont garder le plus petit. Copie les lignes distinctes dans une nouvelle table et remplace l’ancienne. Recrée les index et contraintes de l’ancienne table, car la copie ne les conserve pas :

-- Input: page_views
-- url      | viewed_at
-- ---------+--------------------
-- /pricing | 2026-10-01 09:00:00
-- /pricing | 2026-10-01 09:00:00
-- /docs    | 2026-10-01 09:05:00
-- /pricing | 2026-10-01 09:00:00

-- In SQL Server, use SELECT DISTINCT * INTO page_views_clean FROM page_views,
-- then EXEC sp_rename 'page_views_clean', 'page_views'
CREATE TABLE page_views_clean AS SELECT DISTINCT * FROM page_views;
DROP TABLE page_views;
ALTER TABLE page_views_clean RENAME TO page_views;

-- Output (page_views afterwards):
-- url      | viewed_at
-- ---------+--------------------
-- /pricing | 2026-10-01 09:00:00
-- /docs    | 2026-10-01 09:05:00

Pour aller plus loin sur la suppression des doublons

Sur ce blog :

Documentation officielle :

Beekeeper Studio est une interface de base de données gratuite & open source

Le meilleur outil de requêtes & éditeur SQL que j’aie jamais utilisé. Il m’offre tout ce dont j’ai besoin pour gérer ma base de données. - ⭐⭐⭐⭐⭐ Mit

Beekeeper Studio est rapide, intuitif et facile à utiliser. Beekeeper prend en charge plein de bases de données et fonctionne parfaitement sous Windows, Mac et Linux.

La version Linux de Beekeeper est 100% complète : rien de bâclé, aucun compromis sur les fonctionnalités.

Ce que les utilisateurs disent de Beekeeper Studio

★★★★★
« Beekeeper Studio a complètement remplacé mon ancien workflow SQL. C’est rapide, intuitif, et ça rend le travail sur les bases de données à nouveau agréable. »
— Alex K., développeur base de données
★★★★★
« J’ai essayé beaucoup d’interfaces de base de données, mais Beekeeper trouve l’équilibre parfait entre fonctionnalités et simplicité. Ça marche, tout simplement. »
— Sarah M., ingénieure full stack