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

Pour trouver les doublons en SQL, regroupe les lignes selon les colonnes qui devraient être uniques et garde les groupes de plus d’une ligne. Les fonctions de fenêtrage et les auto-jointures te montrent ensuite les lignes en double elles-mêmes, pour que tu puisses choisir quelle copie garder. Les exemples fonctionnent sous PostgreSQL, MySQL, SQLite et SQL Server.

Comment trouver les doublons en SQL

GROUP BY regroupe les lignes ayant la même valeur, COUNT(*) compte chaque groupe, et HAVING garde les groupes de plus d’une ligne :

SELECT columns, COUNT(*)
FROM table_name
GROUP BY columns
HAVING COUNT(*) > 1;

Par exemple, pour trouver les adresses e-mail utilisées par plusieurs clients :

-- Input: customers
-- id | name           | email
-- ---+----------------+----------------------
-- 1  | Maria Lopez    | maria.lopez@gmail.com
-- 2  | Sam Patel      | sam@patel.dev
-- 3  | M. Lopez       | maria.lopez@gmail.com
-- 4  | Kenji Watanabe | kenji@watanabe.jp

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

-- Output:
-- email                 | copies
-- ----------------------+-------
-- maria.lopez@gmail.com | 2

Pour ne considérer des lignes comme doublons que si plusieurs colonnes correspondent toutes, liste chacune d’elles à la fois dans SELECT et dans GROUP BY.

Les règles à connaître

  • Chaque colonne sélectionnée doit figurer dans GROUP BY ou dans un agrégat. Ajouter name à la requête ci-dessus n’affiche pas les noms des doublons. C’est une erreur, sauf sous SQLite, qui affiche un seul nom par e-mail :
-- Input: the customers table above

SELECT name, email, COUNT(*) AS copies
FROM customers
GROUP BY email
HAVING COUNT(*) > 1;

-- ERROR: column "customers.name" must appear in the GROUP BY clause or be used in an aggregate function

Pour voir chaque ligne en double, utilise l’une des requêtes de la section « Syntaxe alternative ».

  • Les NULL sont regroupés ensemble, et la casse compte. Tous les e-mails NULL tombent dans un même groupe et ressemblent donc à des doublons, alors que Ana@Silva.dev et ana@silva.dev non :
-- Input: users
-- id | email
-- ---+--------------
-- 1  | ana@silva.dev
-- 2  | NULL
-- 3  | Ana@Silva.dev
-- 4  | NULL

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

-- Output:
-- email | copies
-- ------+-------
-- NULL  | 2

-- MySQL and SQL Server ignore case by default, so they also return ana@silva.dev | 2

-- Skip the NULLs and ignore case:
SELECT LOWER(email) AS email, COUNT(*) AS copies
FROM users
WHERE email IS NOT NULL
GROUP BY LOWER(email)
HAVING COUNT(*) > 1;

-- Output:
-- email         | copies
-- --------------+-------
-- ana@silva.dev | 2

Syntaxe alternative

GROUP BY renvoie une ligne par valeur en double. Pour obtenir les lignes en double elles-mêmes, compte plutôt sur une fenêtre. COUNT(*) OVER (PARTITION BY email) place le nombre d’occurrences de chaque e-mail sur toutes les lignes qui le contiennent :

-- Input: the customers table above

SELECT id, name, email, copies
FROM (
  SELECT id, name, email, COUNT(*) OVER (PARTITION BY email) AS copies
  FROM customers
) AS c
WHERE copies > 1
ORDER BY id;

-- Output:
-- id | name        | email                 | copies
-- ---+-------------+-----------------------+-------
-- 1  | Maria Lopez | maria.lopez@gmail.com | 2
-- 3  | M. Lopez    | maria.lopez@gmail.com | 2

EXISTS fait la même chose sans fonction de fenêtrage : garde chaque ligne pour laquelle il existe une autre ligne avec le même e-mail :

-- Input: the customers table above

SELECT id, name, email
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM customers other
  WHERE other.email = c.email AND other.id <> c.id
);

-- Output:
-- id | name        | email
-- ---+-------------+----------------------
-- 1  | Maria Lopez | maria.lopez@gmail.com
-- 3  | M. Lopez    | maria.lopez@gmail.com

Quand chercher des doublons ?

Les doublons arrivent via les imports, les doubles clics, les nouvelles tentatives et les contraintes manquantes. Voici les quatre tâches que tu auras le plus souvent à faire à leur sujet.

Vérifier qu’une colonne est unique

Avant d’ajouter une contrainte UNIQUE, ou quand une connexion ou une recherche se met à renvoyer deux lignes, liste les valeurs qui apparaissent plus d’une fois. GROUP BY avec HAVING y répond directement.

Voir toutes les lignes concernées

Savoir quelle valeur se répète ne suffit pas pour corriger le problème : il te faut les lignes complètes pour voir en quoi elles diffèrent et laquelle est la bonne. Réinjecte les valeurs en double dans une requête sur la même table.

Marquer les copies en trop

Pour faire le ménage, garde une ligne de chaque groupe de doublons et supprime le reste. ROW_NUMBER() numérote les lignes de chaque groupe, donc tout ce qui vient après la ligne 1 est une copie en trop.

Repérer les doubles envois

Un bouton cliqué deux fois ou un paiement relancé laisse deux lignes identiques à part leur ID. Joindre la table à elle-même les associe par paires, et t1.id < t2.id liste chaque paire une seule fois.

Exemples concrets de recherche de doublons

Trouver les chambres d’hôtel réservées deux fois

Une chambre ne peut être réservée qu’une fois par nuit, donc un doublon correspond à la même chambre pour la même nuit. Regroupe sur les deux colonnes :

-- Input: bookings
-- booking_id | room | night      | guest
-- -----------+------+------------+----------
-- 1          | 204  | 2026-11-06 | R. Okafor
-- 2          | 204  | 2026-11-07 | R. Okafor
-- 3          | 310  | 2026-11-06 | L. Chen
-- 4          | 204  | 2026-11-06 | P. Novak

SELECT room, night, COUNT(*) AS bookings
FROM bookings
GROUP BY room, night
HAVING COUNT(*) > 1;

-- Output:
-- room | night      | bookings
-- -----+------------+---------
-- 204  | 2026-11-06 | 2

Lister les comptes qui partagent une adresse e-mail

Certaines personnes s’inscrivent deux fois avec la même adresse écrite avec une casse différente. Partitionne par LOWER(email) pour afficher les deux comptes côte à côte :

-- Input: accounts
-- id | username | email
-- ---+----------+----------------
-- 1  | jkim     | jin@kim.kr
-- 2  | olivia_r | olivia@reyes.mx
-- 3  | jin.kim  | Jin@Kim.kr
-- 4  | noah_b   | noah@baker.uk

SELECT id, username, email, accounts_with_email
FROM (
  SELECT id, username, email,
         COUNT(*) OVER (PARTITION BY LOWER(email)) AS accounts_with_email
  FROM accounts
) AS a
WHERE accounts_with_email > 1
ORDER BY id;

-- Output:
-- id | username | email      | accounts_with_email
-- ---+----------+------------+--------------------
-- 1  | jkim     | jin@kim.kr | 2
-- 3  | jin.kim  | Jin@Kim.kr | 2

Signaler les personnes en double avant un nettoyage

Considère les utilisateurs ayant les mêmes prénom et nom comme une seule personne, garde l’enregistrement le plus ancien et étiquette les autres. Une CTE sépare la numérotation de l’étiquetage :

-- Input: users
-- id | first_name | last_name | email
-- ---+------------+-----------+----------------
-- 1  | Priya      | Shah      | priya@shah.net
-- 2  | Tom        | Becker    | tom@becker.de
-- 3  | Priya      | Shah      | pshah@work.com
-- 4  | Lena       | Kowalski  | lena@kowal.pl
-- 5  | Tom        | Becker    | tbecker@mail.de

WITH numbered AS (
  SELECT id, first_name, last_name, email,
         ROW_NUMBER() OVER (PARTITION BY first_name, last_name ORDER BY id) AS copy_number
  FROM users
)
SELECT id, first_name, last_name, email,
       CASE WHEN copy_number > 1 THEN 'Duplicate' ELSE 'Original' END AS status
FROM numbered
ORDER BY id;

-- Output:
-- id | first_name | last_name | email           | status
-- ---+------------+-----------+-----------------+----------
-- 1  | Priya      | Shah      | priya@shah.net  | Original
-- 2  | Tom        | Becker    | tom@becker.de   | Original
-- 3  | Priya      | Shah      | pshah@work.com  | Duplicate
-- 4  | Lena       | Kowalski  | lena@kowal.pl   | Original
-- 5  | Tom        | Becker    | tbecker@mail.de | Duplicate

Les lignes marquées Duplicate sont celles à fusionner ou supprimer. Le guide pour supprimer les doublons explique comment les supprimer.

Trouver les commandes passées deux fois par erreur

Deux commandes du même client pour le même produit le même jour sont probablement une seule commande envoyée deux fois :

-- Input: orders
-- order_id | customer_id | product   | order_date
-- ---------+-------------+-----------+-----------
-- 1001     | 42          | Desk Lamp | 2026-03-02
-- 1002     | 42          | Desk Lamp | 2026-03-02
-- 1003     | 42          | Desk Lamp | 2026-03-09
-- 1004     | 57          | Desk Lamp | 2026-03-02

SELECT o1.order_id AS first_order, o2.order_id AS repeat_order,
       o1.customer_id, o1.product
FROM orders o1
JOIN orders o2
  ON o1.customer_id = o2.customer_id
 AND o1.product = o2.product
 AND o1.order_date = o2.order_date
 AND o1.order_id < o2.order_id;

-- Output:
-- first_order | repeat_order | customer_id | product
-- ------------+--------------+-------------+----------
-- 1001        | 1002         | 42          | Desk Lamp

Pour aller plus loin sur les 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 agréable à nouveau. »
— 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