Trovare valori duplicati in MySQL


769

Ho una tabella con una colonna varchar e vorrei trovare tutti i record che hanno valori duplicati in questa colonna. Qual è la migliore query che posso usare per trovare i duplicati?


1
Dato che hai menzionato trova tutti i record, presumo che tu debba conoscere i TASTI e i VALORI duplicati in quella colonna varchar.
TechTravel Pensa al

Riesco a trovare le chiavi abbastanza facilmente dopo aver ottenuto i valori, voglio solo un elenco di tutti i valori duplicati.
Jon Tackabury,

Risposte:


1522

Fai un SELECTcon una GROUP BYclausola. Diciamo che il nome è la colonna in cui vuoi trovare i duplicati:

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

Ciò restituirà un risultato con il valore del nome nella prima colonna e un conteggio di quante volte quel valore appare nella seconda.


27
Ma come è utile se non riesci a ottenere gli ID delle righe con valori duplicati? Sì, è possibile eseguire una nuova query corrispondente per ciascun valore duplicato, ma è possibile elencare semplicemente i duplicati?
NobleUplift,

23
@NobleUplift Puoi fare un GROUP_CONCAT(id)ed elencherà gli ID. Vedi la mia risposta per un esempio.
Matt Rardon,

5
Cosa significherebbe se dicesse ERROR: column "c" does not exist LINE 1?
Utente

15
Sono confuso perché questa è la risposta accettata e perché ha così tanti voti positivi. L'OP ha chiesto: "Vorrei trovare tutti i record con valori duplicati in questa colonna". Questa risposta restituisce una tabella di conteggi. -1
Monica Heddneck,

4
Per coloro che non capiscono come funziona HAVING: è semplicemente un filtro sul set di risultati, quindi succede dopo la query principale.
John Hunt,

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

10
Superiore alla risposta di @ levik poiché non aggiunge una colonna aggiuntiva. Lo rende utile per l'uso con 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
        )

Questa query restituisce record completi, non solo di quelli distinti varchar_column.

Questa query non utilizza COUNT(*). Se ci sono molti duplicati, COUNT(*)è costoso e non hai bisogno del tutto COUNT(*), devi solo sapere se ci sono due righe con lo stesso valore.

Avere un indice varchar_columnattivo ovviamente accelererà notevolmente questa query.


3
Molto bene. Ho aggiunto ORDER BY varchar_column DESCalla fine della query.
trante,

8
Questa dovrebbe essere la risposta accettata, come GROUP BYe HAVINGrestituisce solo uno dei possibili duplicati. Inoltre, prestazioni con campo indicizzato anziché COUNT(*)e possibilità ORDER BYdi raggruppare record duplicati.
Rémi Breton,

1
Come indicato nei commenti sopra, questa query consente di elencare tutte le righe duplicate. Molto utile.
Prova Hardard il

4
Guardando questo non capisco come funzionerebbe affatto. La condizione interna non sarà sempre vera poiché qualsiasi riga nella tabella esterna sarà disponibile anche nella tabella interna e quindi ogni riga corrisponderà sempre almeno a se stessa? Ho provato la query e ho ottenuto il risultato che sospettavo: ogni riga restituita. Ma con così tanti voti dubito di me stesso. Alla query interna manca qualcosa come "AND mto.id <> mti.id"? Funziona per me quando lo aggiungo.
Clox,

2
@Quassnoi Va bene. Ho provato a metterlo su sqlfiddle ma ho rinunciato da quando ogni query che provo a eseguire, a parte la creazione dello schema, è scaduta. Ho capito che la semplice rimozione di "EXISTS" fa funzionare correttamente anche la query.
Clox,

144

Costruendo la risposta di levik per ottenere gli ID delle righe duplicate, puoi fare un GROUP_CONCATcaso se il tuo server lo supporta (questo restituirà un elenco di ID separati da virgole).

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

12
Tutto questo tempo senza sapere di GROUP_CONCAT ()! molto molto utile.
aede

Molto apprezzato Matt. Questo è veramente utile! Per coloro che cercano di aggiornare in phpmyadmin se si lascia l'id insieme alla funzione in questo modo: SELECT id, GROUP_CONCAT(id), name, COUNT(*) c [...]abilita la modifica in linea e dovrebbe aggiornare tutte le righe coinvolte (o almeno la prima corrispondente), ma sfortunatamente la modifica genera un errore Javascript. ..
Armfoot,

Come calcoleresti quanti ID sono soggetti a duplicazione?
CMCDragonkai,

2
Come faccio a non raggruppare tutti gli ID ma a elencarli dal primo all'ultimo; con tutti i rispettivi valori nelle colonne accanto a loro? Quindi, invece di raggrupparlo, mostra solo ID 1 e il suo valore, ID 2 e il suo valore. ANCHE se i valori per l'ID sono uguali.
MailBlade,

1
Risposta estremamente utile, questa dovrebbe essere la migliore, quindi più persone la vedono. Ricordo quanto dolore provai durante la creazione di tali elenchi, ed era sempre disponibile come comando ..
Giovanni,

13

Supponendo che la tabella sia denominata TableABC e la colonna desiderata sia Col e la chiave primaria per T1 è Key.

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

Il vantaggio di questo approccio rispetto alla risposta sopra è che fornisce la chiave.


4
+1 Perché è utile. Per ironia della sorte, il risultato stesso contiene duplicati (elenca aeb, quindi
bec a

2
@FabienSnauwaert Puoi sbarazzarti di alcuni dei duplicati confrontando meno di (o maggiore di)
Michael

@TechTravel Pensa che la tua risposta sia molto chiara, grazie per quello, ma su un grande tavolo ci vuole un po 'di tempo (circa 2 minuti su più di 20'000 voci di tabella) e dopo mostrare 25 primi risultati, se faccio clic per mostrare il prossimo, phpmyadmin show error "# 1052 - La colonna "id" nella clausola order è ambigua "
bcag2

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

1
No, perché questo è probabilmente il più lento del lotto. Le sottoselezioni sono notoriamente lente, poiché vengono eseguite per ogni riga restituita.
Oddman,

10

Per trovare quanti record sono duplicati nella colonna del nome in Employee, la query di seguito è utile;

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

10

per ottenere tutti i dati che contengono duplicazioni ho usato questo:

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 tabella con cui stai lavorando.

DupliactedData = i dati duplicati che stai cercando.


Questo mostra ogni duplicato nella sua riga. Quello è ciò di cui ho bisogno. Grazie.
warmwhisky il

8

La mia query finale includeva alcune delle risposte che mi hanno aiutato - combinando gruppo per, conteggio e GROUP_CONCAT.

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

Ciò fornisce l'id di entrambi gli esempi (separati da virgola), il codice a barre di cui avevo bisogno e quanti duplicati.

Modifica la tabella e le colonne di conseguenza.


8

Non vedo alcun approccio JOIN, che ha molti usi in termini di duplicati.

Questo approccio offre risultati raddoppiati.

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
Cordiali saluti - Ti consigliamo di "selezionare somecol distinti .." se esiste la possibilità che esista più di 1 record duplicato, altrimenti i risultati conterranno duplicati delle righe duplicate che sono state trovate.
Estratto il

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

Sostituisci la città con il tuo tavolo. Sostituisci il nome con il nome del campo



6

Ho visto il risultato sopra e la query funzionerà bene se è necessario controllare il valore di una singola colonna che è duplicato. Ad esempio e-mail.

Ma se è necessario verificare con più colonne e si desidera verificare la combinazione del risultato, questa query funzionerà correttamente:

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)

Esattamente quello che serviva! Ecco la mia domanda, controllando 3 campi per i duplicati: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

Preferisco usare le funzioni con finestra (MySQL 8.0+) per trovare duplicati perché ho potuto vedere l'intera riga:

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
Fare la stessa subquery due volte sembra inefficiente.
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
Vale la pena notare che questo è insopportabilmente lento o potrebbe anche non finire se la colonna per cui viene eseguita la query non è indicizzata. Altrimenti, sono stato in grado di passare a.emaila a.*e ottenere tutti gli ID delle righe con i duplicati.
NobleUplift,

@NobleUplift Di cosa stai parlando?
Michael,

@Michael Beh, dato che ho tre anni, non posso testare su qualunque versione di MySQL stavo usando, ma ho provato questa stessa query su un database in cui la colonna che avevo selezionato non aveva un indice, quindi ci sono voluti parecchi pochi secondi per finire. Modificandolo in SELECT DISTINCT a.*risolto quasi istantaneamente.
NobleUplift,

@NobleUplift Ah ok. Posso capire che è lento ... la parte che mi preoccupa è "potrebbe non finire nemmeno".
Michael,

@Michael Non ricordo su quale tabella nel nostro sistema dovevo eseguire questa query, ma per quelli con qualche milione di dischi probabilmente avrebbero finito, ma in un tempo che ha impiegato così tanto tempo che ho rinunciato a vedere quando in realtà sarebbe finito.
NobleUplift,

1

Per rimuovere le righe duplicate con più campi, prima cancellale nella nuova chiave univoca specificata per le sole righe distinte, quindi usa il comando "raggruppa per" per rimuovere le righe duplicate con la stessa nuova chiave univoca:

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;

puoi anche aggiungere una spiegazione?
Robert,

Perché non usare CREATE TEMPORARY TABLE ...? Una piccola spiegazione della tua soluzione sarebbe ottima.
maxhb

1

Un contributo molto tardi ... nel caso in cui aiuti qualcuno a smettere di fumare ... Avevo il compito di trovare coppie di transazioni corrispondenti (in realtà entrambi i lati dei trasferimenti da conto a conto) in un'app bancaria, per identificare quali erano il 'da' e il 'a' per ogni transazione di trasferimento tra account, quindi abbiamo finito con questo:

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;

Il risultato è che DuplicateResultsTablefornisce righe contenenti transazioni corrispondenti (cioè duplicate), ma fornisce anche gli stessi ID transazione al contrario la seconda volta che corrisponde alla stessa coppia, quindi l'esterno SELECTè lì per raggruppare per il primo ID transazione, che viene fatto usando LEASTe GREATESTper assicurarsi che i due transazioniid siano sempre nello stesso ordine nei risultati, il che lo rende sicuro GROUPdal primo, eliminando così tutte le corrispondenze duplicate. Ha superato quasi un milione di record e identificato oltre 12.000 partite in poco meno di 2 secondi. Ovviamente il Transactionid è l'indice primario, che ha davvero aiutato.


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
Ciò non è corretto poiché trova anche occorrenze uniche. 0 dovrebbe essere 1.
Kafoso

1

Se si desidera rimuovere l'uso duplicato DISTINCT

Altrimenti usa questa query:

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


0

Prova a usare questa query:

SELECT name, COUNT(*) value_count FROM company_master GROUP BY name HAVING value_count > 1;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.