Trouver des valeurs en double dans MySQL


770

J'ai une table avec une colonne varchar, et je voudrais trouver tous les enregistrements qui ont des valeurs en double dans cette colonne. Quelle est la meilleure requête que je puisse utiliser pour trouver les doublons?


1
Puisque vous avez mentionné trouver tous les enregistrements, je suppose que vous devez connaître les CLÉS ainsi que les VALEURS dupliquées dans cette colonne varchar.
TechTravelThink

Je peux trouver les clés assez facilement après avoir obtenu les valeurs, je veux vraiment juste une liste de toutes les valeurs en double.
Jon Tackabury

Réponses:


1523

Faites un SELECTavec une GROUP BYclause. Disons que le nom est la colonne dans laquelle vous souhaitez trouver des doublons:

SELECT name, COUNT(*) c FROM table GROUP BY name HAVING c > 1;

Cela renverra un résultat avec la valeur du nom dans la première colonne et un nombre de fois que cette valeur apparaîtra dans la seconde.


27
Mais comment est-ce utile si vous ne pouvez pas obtenir les ID des lignes avec des valeurs en double? Oui, vous pouvez faire une nouvelle requête correspondant à chaque valeur en double, mais est-il possible de simplement lister les doublons?
NobleUplift

23
@NobleUplift Vous pouvez faire un GROUP_CONCAT(id)et il listera les identifiants. Voir ma réponse pour un exemple.
Matt Rardon

5
Qu'est-ce que cela signifierait s'il disait ERROR: column "c" does not exist LINE 1?
Utilisateur

15
Je ne comprends pas pourquoi c'est la réponse acceptée et pourquoi il y a tant de votes positifs. L'OP a demandé: "Je voudrais trouver tous les enregistrements qui ont des valeurs en double dans cette colonne." Cette réponse renvoie une table de décomptes. -1
Monica Heddneck

4
Pour ceux qui ne comprennent pas comment fonctionne HAVING - il s'agit simplement d'un filtre sur l'ensemble de résultats, ce qui se produit après la requête principale.
John Hunt

236
SELECT varchar_col
FROM table
GROUP BY varchar_col
HAVING COUNT(*) > 1;

10
Supérieur à la réponse de @ levik car il n'ajoute pas de colonne supplémentaire. Le rend utile pour une utilisation avec IN()/ NOT IN().
wmassingham

172
SELECT  *
FROM    mytable mto
WHERE   EXISTS
        (
        SELECT  1
        FROM    mytable mti
        WHERE   mti.varchar_column = mto.varchar_column
        LIMIT 1, 1
        )

Cette requête renvoie des enregistrements complets, pas seulement des enregistrements distincts varchar_column.

Cette requête n'utilise pas COUNT(*). S'il y a beaucoup de doublons, COUNT(*)coûte cher et que vous n'avez pas besoin du tout COUNT(*), il vous suffit de savoir s'il y a deux lignes de même valeur.

varchar_columnBien entendu, avoir un index accélérera considérablement cette requête.


3
Très bien. J'ai ajouté ORDER BY varchar_column DESCà la fin de la requête.
trante

8
Cela devrait être la réponse acceptée, car GROUP BYet HAVINGne renvoie qu'un seul des doublons possibles. En outre, les performances avec un champ indexé au lieu de COUNT(*), et la possibilité ORDER BYde regrouper les enregistrements en double.
Rémi Breton

1
Comme indiqué dans les commentaires ci-dessus, cette requête vous permet de répertorier toutes les lignes dupliquées. Très utile.
TryHarder

4
En regardant cela, je ne comprends pas du tout comment cela fonctionnerait. La condition interne ne sera-t-elle pas toujours vraie car toute ligne de la table externe sera également disponible dans la table interne et donc chaque ligne correspondra au moins toujours à elle-même? J'ai essayé la requête et obtenu le résultat que je soupçonnais - chaque ligne est revenue. Mais avec tant de votes positifs, je doute moi-même. La requête interne ne manque-t-elle pas quelque chose comme "AND mto.id <> mti.id"? Cela fonctionne pour moi lorsque j'ajoute cela.
Clox

2
@Quassnoi Très bien. J'ai essayé de le mettre sur sqlfiddle mais j'ai abandonné depuis que chaque requête que j'essaie d'exécuter, à part la création du schéma, arrive à expiration. J'ai compris que la simple suppression de "EXISTS" fait également fonctionner correctement la requête pour moi.
Clox

144

À partir de la réponse de levik pour obtenir les ID des lignes en double, vous pouvez le faire GROUP_CONCATsi votre serveur le prend en charge (cela renverra une liste d'ID séparés par des virgules).

SELECT GROUP_CONCAT(id), name, COUNT(*) c FROM documents GROUP BY name HAVING c > 1;

12
Tout ce temps sans connaître GROUP_CONCAT ()! très très utile.
aesede

Vraiment apprécié Matt. C'est vraiment utile! Pour ceux qui essaient de mettre à jour dans phpmyadmin si vous laissez l'identifiant avec la fonction comme ceci: SELECT id, GROUP_CONCAT(id), name, COUNT(*) c [...]il permet la modification en ligne et il devrait mettre à jour toutes les lignes impliquées (ou au moins la première correspondante), mais malheureusement, la modification génère une erreur Javascript. ..
Armfoot

Comment calculeriez-vous alors combien d'ID sont sujets à duplication?
CMCDragonkai

2
Comment puis-je ne pas regrouper tous les ID, mais plutôt les répertorier du premier au dernier; avec toutes leurs valeurs respectives dans les colonnes à côté d'eux? Ainsi, au lieu de le regrouper, il affiche simplement l'ID 1 et sa valeur, l'ID 2 et sa valeur. MÊME si les valeurs de l'ID sont identiques.
MailBlade

1
Réponse extrêmement utile, cela devrait être top pour que plus de gens la voient. Je me souviens de la peine que j'ai éprouvée à créer de telles listes, et elles étaient disponibles tout le temps en tant que commande.
John

13

En supposant que votre table est nommée TableABC et la colonne que vous souhaitez est Col et la clé primaire de T1 est Key.

SELECT a.Key, b.Key, a.Col 
FROM TableABC a, TableABC b
WHERE a.Col = b.Col 
AND a.Key <> b.Key

L'avantage de cette approche sur la réponse ci-dessus est qu'elle donne la clé.


4
+1 Parce que c'est pratique. Cependant, ironiquement, le résultat lui-même contient des doublons (il répertorie a et b, puis b et a.)
Fabien Snauwaert

2
@FabienSnauwaert Vous pouvez vous débarrasser de certains des doublons en comparant moins que (ou plus que)
Michael

@TechTravelThink, votre réponse est très claire, merci pour cela, mais sur une grande table, cela prend du temps (environ 2mn sur plus de 20'000 entrées) et après afficher 25 premiers résultats, si je clique pour afficher le suivant, phpmyadmin affiche l'erreur "# 1052 - La colonne 'id' dans la clause de commande est ambiguë "
bcag2

12
SELECT * 
FROM `dps` 
WHERE pid IN (SELECT pid FROM `dps` GROUP BY pid HAVING COUNT(pid)>1)

1
Non, car c'est probablement la plus lente du lot. Les sous-sélections sont notoirement lentes, car elles sont exécutées pour chaque ligne renvoyée.
Oddman

10

Pour trouver combien d'enregistrements sont des doublons dans la colonne de nom dans Employé, la requête ci-dessous est utile;

Select name from employee group by name having count(*)>1;

10

pour obtenir toutes les données contenant des doublons, j'ai utilisé ceci:

SELECT * FROM TableName INNER JOIN(
  SELECT DupliactedData FROM TableName GROUP BY DupliactedData HAVING COUNT(DupliactedData) > 1 order by DupliactedData)
  temp ON TableName.DupliactedData = temp.DupliactedData;

TableName = la table avec laquelle vous travaillez.

DupliactedData = les données dupliquées que vous recherchez.


Celui-ci montre chaque doublon dans sa propre ligne. C'est ce dont j'ai besoin. Merci.
warmwhisky

8

Ma dernière requête a incorporé ici quelques-unes des réponses qui ont aidé - en combinant group by, count & GROUP_CONCAT.

SELECT GROUP_CONCAT(id), `magento_simple`, COUNT(*) c 
FROM product_variant 
GROUP BY `magento_simple` HAVING c > 1;

Cela fournit l'id des deux exemples (séparés par des virgules), le code à barres dont j'avais besoin et le nombre de doublons.

Modifiez le tableau et les colonnes en conséquence.


8

Je ne vois aucune approche JOIN, qui a de nombreuses utilisations en termes de doublons.

Cette approche vous donne des résultats réels doublés.

SELECT t1.* FROM my_table as t1 
LEFT JOIN my_table as t2 
ON t1.name=t2.name and t1.id!=t2.id 
WHERE t2.id IS NOT NULL 
ORDER BY t1.name

2
FYI - Vous voudrez «sélectionner un somecol distinct ..» s'il existe un potentiel pour plus d'un enregistrement en double, sinon les résultats contiendront des doublons des lignes dupliquées trouvées.
Drew

7
SELECT t.*,(select count(*) from city as tt
  where tt.name=t.name) as count
  FROM `city` as t
  where (
     select count(*) from city as tt
     where tt.name=t.name
  ) > 1 order by count desc

Remplacez la ville par votre table. Remplacez le nom par le nom de votre champ


7

Pour aller plus loin dans la réponse de @ maxyfc , j'avais besoin de trouver toutes les lignes renvoyées avec les valeurs en double, afin de pouvoir les modifier dans MySQL Workbench :

SELECT * FROM table
   WHERE field IN (
     SELECT field FROM table GROUP BY field HAVING count(*) > 1
   ) ORDER BY field

6

J'ai vu le résultat ci-dessus et la requête fonctionnera bien si vous devez vérifier la valeur d'une seule colonne qui est en double. Par exemple, e-mail.

Mais si vous avez besoin de vérifier avec plus de colonnes et que vous souhaitez vérifier la combinaison du résultat, cette requête fonctionnera correctement:

SELECT COUNT(CONCAT(name,email)) AS tot,
       name,
       email
FROM users
GROUP BY CONCAT(name,email)
HAVING tot>1 (This query will SHOW the USER list which ARE greater THAN 1
              AND also COUNT)

Exactement ce qui était nécessaire! Voici ma requête, en vérifiant 3 champs pour les doublons:SELECT COUNT(CONCAT(userid,event,datetime)) AS total, userid, event, datetime FROM mytable GROUP BY CONCAT(userid, event, datetime ) HAVING total>1
Kai Noack

4

Je préfère utiliser des fonctions fenêtrées (MySQL 8.0+) pour trouver des doublons car je pouvais voir la ligne entière:

WITH cte AS (
  SELECT *
    ,COUNT(*) OVER(PARTITION BY col_name) AS num_of_duplicates_group
    ,ROW_NUMBER() OVER(PARTITION BY col_name ORDER BY col_name2) AS pos_in_group
  FROM table
)
SELECT *
FROM cte
WHERE num_of_duplicates_group > 1;

DB Fiddle Demo


3
SELECT 
    t.*,
    (SELECT COUNT(*) FROM city AS tt WHERE tt.name=t.name) AS count 
FROM `city` AS t 
WHERE 
    (SELECT count(*) FROM city AS tt WHERE tt.name=t.name) > 1 ORDER BY count DESC

1
Faire deux fois la même sous-requête semble inefficace.
NobleUplift


3
CREATE TABLE tbl_master
    (`id` int, `email` varchar(15));

INSERT INTO tbl_master
    (`id`, `email`) VALUES
    (1, 'test1@gmail.com'),
    (2, 'test2@gmail.com'),
    (3, 'test1@gmail.com'),
    (4, 'test2@gmail.com'),
    (5, 'test5@gmail.com');

QUERY : SELECT id, email FROM tbl_master
WHERE email IN (SELECT email FROM tbl_master GROUP BY email HAVING COUNT(id) > 1)

2
SELECT DISTINCT a.email FROM `users` a LEFT JOIN `users` b ON a.email = b.email WHERE a.id != b.id;

1
Il convient de noter que cela est insupportablement lent ou pourrait même ne pas se terminer si la colonne interrogée n'est pas indexée. Sinon, j'ai pu passer a.emailà a.*et obtenir tous les ID des lignes avec des doublons.
NobleUplift

@NobleUplift De quoi parlez-vous?
Michael

@Michael Eh bien, puisque cela fait trois ans, je ne peux pas tester sur quelle version de MySQL que j'utilisais, mais j'ai essayé cette même requête sur une base de données où la colonne que je sélectionnais n'avait pas d'index, donc ça a pris pas mal de temps quelques secondes pour terminer. Le changer pour SELECT DISTINCT a.*résolu presque instantanément.
NobleUplift

@NobleUplift Ah ok. Je peux comprendre que ce soit lent ... la partie qui m'inquiète est "pourrait même ne pas finir".
Michael

@Michael Je ne me souviens pas sur quelle table de notre système je devais exécuter cette requête, mais pour ceux avec quelques millions d'enregistrements, ils auraient probablement fini, mais en un temps si long que j'ai abandonné de voir quand cela finirait en fait.
NobleUplift

1

Pour supprimer les lignes en double avec plusieurs champs, annulez-les d'abord à la nouvelle clé unique qui est spécifiée pour les seules lignes distinctes, puis utilisez la commande "group by" pour supprimer les lignes en double avec la même nouvelle clé unique:

Create TEMPORARY table tmp select concat(f1,f2) as cfs,t1.* from mytable as t1;
Create index x_tmp_cfs on tmp(cfs);
Create table unduptable select f1,f2,... from tmp group by cfs;

pouvez-vous également ajouter une explication?
Robert

Pourquoi ne pas utiliser CREATE TEMPORARY TABLE ...? Une petite explication de votre solution serait formidable.
maxhb

1

Une contribution très tardive ... au cas où cela aiderait quiconque à waaaaaay sur la ligne ... J'ai eu la tâche de trouver des paires de transactions correspondantes (en fait des deux côtés des transferts de compte à compte) dans une application bancaire, pour identifier celles étaient le «de» et le «à» pour chaque transaction de transfert entre comptes, nous nous sommes donc retrouvés avec ceci:

SELECT 
    LEAST(primaryid, secondaryid) AS transactionid1,
    GREATEST(primaryid, secondaryid) AS transactionid2
FROM (
    SELECT table1.transactionid AS primaryid, 
        table2.transactionid AS secondaryid
    FROM financial_transactions table1
    INNER JOIN financial_transactions table2 
    ON table1.accountid = table2.accountid
    AND table1.transactionid <> table2.transactionid 
    AND table1.transactiondate = table2.transactiondate
    AND table1.sourceref = table2.destinationref
    AND table1.amount = (0 - table2.amount)
) AS DuplicateResultsTable
GROUP BY transactionid1
ORDER BY transactionid1;

Le résultat est que le DuplicateResultsTablefournit des lignes contenant des transactions correspondantes (c'est-à-dire en double), mais il fournit également les mêmes identifiants de transaction en sens inverse la deuxième fois qu'il correspond à la même paire, de sorte que l'extérieur SELECTest là pour regrouper par le premier ID de transaction, ce qui est fait en utilisant LEASTet GREATESTpour vous assurer que les deux transactionid sont toujours dans le même ordre dans les résultats, ce qui le rend sûr GROUPpar le premier, éliminant ainsi toutes les correspondances en double. Parcourez près d'un million de records et identifié plus de 12 000 matchs en un peu moins de 2 secondes. Bien sûr, le transactionid est l'indice principal, ce qui a vraiment aidé.


1
Select column_name, column_name1,column_name2, count(1) as temp from table_name group by column_name having temp > 1

1
SELECT ColumnA, COUNT( * )
FROM Table
GROUP BY ColumnA
HAVING COUNT( * ) > 1

3
Ceci est incorrect car il trouve également des occurrences uniques. 0 devrait être 1.
Kafoso

1

Si vous souhaitez supprimer l'utilisation en double DISTINCT

Sinon, utilisez cette requête:

SELECT users.*,COUNT(user_ID) as user FROM users GROUP BY user_name HAVING user > 1;


0

Essayez d'utiliser cette requête:

SELECT name, COUNT(*) value_count FROM company_master GROUP BY name HAVING value_count > 1;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.