Suchen Sie doppelte Datensätze in MySQL


650

Ich möchte doppelte Datensätze in einer MySQL-Datenbank abrufen. Dies kann erfolgen mit:

SELECT address, count(id) as cnt FROM list
GROUP BY address HAVING cnt > 1

Was in ... resultiert:

100 MAIN ST    2

Ich möchte es so ziehen, dass es jede Zeile zeigt, die ein Duplikat ist. Etwas wie:

JIM    JONES    100 MAIN ST
JOHN   SMITH    100 MAIN ST

Irgendwelche Gedanken darüber, wie das gemacht werden kann? Ich versuche zu vermeiden, den ersten zu machen und dann die Duplikate mit einer zweiten Abfrage im Code nachzuschlagen.

Antworten:


684

Der Schlüssel besteht darin, diese Abfrage neu zu schreiben, damit sie als Unterabfrage verwendet werden kann.

SELECT firstname, 
   lastname, 
   list.address 
FROM list
   INNER JOIN (SELECT address
               FROM   list
               GROUP  BY address
               HAVING COUNT(id) > 1) dup
           ON list.address = dup.address;

69
Seien Sie vorsichtig mit Unterabfragen. Unterabfragen sind / können aus Leistungsgründen lächerlich schlecht sein. Wenn dies häufig und / oder mit vielen doppelten Datensätzen geschehen muss, würde ich in Betracht ziehen, die Verarbeitung aus der Datenbank in einen Datensatz zu verschieben.
Bdwakefield

11
Es ist eine unkorrelierte Unterabfrage, daher sollte es nicht so schlimm sein, wenn eine der beiden Abfragen allein nicht schlecht gestaltet ist.
12ıu

Schön. Vermutlich ist dies die Sytax um "ERROR 1248 (42000): Jede abgeleitete Tabelle muss einen eigenen Alias ​​haben"
doublejosh

3
Dies ist die richtige Idee, aber wie unten funktioniert dies nur, wenn die Adressen garantiert standardisiert sind ...
Matt

30
+1 mit dieser Abfrage können Sie Duplikate, aber auch Dreifach-, Vierfach- usw. finden
Albanx

352
SELECT date FROM logs group by date having count(*) >= 2

5
Dies war die am einfachsten zu verwendende Abfrage für Laravel. Musste nur zur ->having(DB::raw('count(*)'), '>', 2)Abfrage hinzufügen . Danke vielmals!
Kovah

1
Funktioniert gut mit 10 Millionen Zeilen Tabelle. Dies sollte die beste Antwort sein
Terry Lin

13
Seien Sie vorsichtig mit dieser Antwort. Es wird nur eines der Duplikate zurückgegeben. Wenn Sie mehr als 2 Kopien desselben Datensatzes haben, werden nicht alle angezeigt. Nach dem Löschen des zurückgegebenen Datensatzes befinden sich weiterhin Duplikate in Ihrer Tabelle.
Mikiko Jane

7
Warum >=2? Verwenden HAVING COUNT(*) > 1
Sie

2
@TerryLin In Anbetracht dessen, dass dies das ursprünglich angegebene Problem (bei dem alle Duplikate zurückgegeben wurden) nicht wirklich löst, bin ich anderer Meinung.
Michael

198

Warum nicht einfach INNER mit sich selbst auf den Tisch treten?

SELECT a.firstname, a.lastname, a.address
FROM list a
INNER JOIN list b ON a.address = b.address
WHERE a.id <> b.id

Ein DISTINCT wird benötigt, wenn die Adresse mehr als zweimal vorhanden sein könnte.


20
Auch ich habe dies getestet und es war fast sechsmal langsamer als die in meiner Situation akzeptierte Lösung (neueste MySQL, Tabelle mit 120.000 Zeilen). Dies kann daran liegen, dass eine temporäre Tabelle erforderlich ist. Führen Sie auf beiden eine EXPLAIN aus, um die Unterschiede zu erkennen.

4
Ich habe den letzten Teil der Abfrage so geändert WHERE a.id > b.id, dass nur neuere Duplikate herausgefiltert werden. Auf diese Weise kann ich DELETEdirekt auf das Ergebnis zugreifen. Wechseln Sie den Vergleich, um die älteren Duplikate aufzulisten.
Stoffe

1
Die Ausführung dauerte 50 Sekunden, die Antwort von @ doublejosh dauerte 0,13 Sekunden.
Antonagestam

Ich muss hinzufügen, dass diese Antwort trotz WHERE doppelte Antworten liefert, da bei einer Verdreifachung einer Adresse die Ausgabezeilen verdoppelt werden. Wenn es vierfach ist, glaube ich, dass sich die Antwort verdreifacht.
Wli

Ich habe dies in leetcode " leetcode.com/problems/duplicate-emails " getestet . Es war schneller als die Unterabfrage.
Woge

56

Ich habe versucht, die beste Antwort für diese Frage zu finden, aber es hat mich etwas verwirrt. Ich brauchte das eigentlich nur auf einem einzigen Feld von meinem Tisch. Das folgende Beispiel von diesem Link hat für mich sehr gut funktioniert:

SELECT COUNT(*) c,title FROM `data` GROUP BY title HAVING c > 1;

Klappt wunderbar!
Vinícius

47
select `cityname` from `codcities` group by `cityname` having count(*)>=2

Dies ist die ähnliche Abfrage, nach der Sie gefragt haben, und sie funktioniert zu 200% und ist auch einfach. Genießen!!!


37

Ist das nicht einfacher:

SELECT *
FROM tc_tariff_groups
GROUP BY group_id
HAVING COUNT(group_id) >1

?


1
arbeitete für mich, wo ich nur ~ 10 000 doppelte Zeilen verarbeiten musste, um sie eindeutig zu machen, viel schneller als alle 600 000 Zeilen zu laden.
AdrianTNT

1
sehr viel einfacher
Shwet

35

Finden Sie mit dieser Abfrage doppelte Benutzer anhand der E-Mail-Adresse ...

SELECT users.name, users.uid, users.mail, from_unixtime(created)
FROM users
INNER JOIN (
  SELECT mail
  FROM users
  GROUP BY mail
  HAVING count(mail) > 1
) dupes ON users.mail = dupes.mail
ORDER BY users.mail;

2
Um das tatsächliche Duplikat zu finden, benötigen Sie nur die innere Abfrage. Dies ist viel schneller als die anderen Antworten.
Antonagestam

20

Wir können feststellen, dass die Duplikate auch von mehr als einem Feld abhängen. In diesen Fällen können Sie das folgende Format verwenden.

SELECT COUNT(*), column1, column2 
FROM tablename
GROUP BY column1, column2
HAVING COUNT(*)>1;

16

Das Auffinden doppelter Adressen ist viel komplexer als es scheint, insbesondere wenn Sie Genauigkeit benötigen. Eine MySQL-Abfrage reicht in diesem Fall nicht aus ...

Ich arbeite bei SmartyStreets , wo wir uns mit Validierung und und anderen befassen, und ich habe viele verschiedene Herausforderungen mit ähnlichen Problemen gesehen.

Es gibt mehrere Dienste von Drittanbietern, die Duplikate in einer Liste für Sie markieren. Wenn Sie dies ausschließlich mit einer MySQL-Unterabfrage tun, werden Unterschiede in den Adressformaten und -standards nicht berücksichtigt. Der USPS (für US-Adresse) hat bestimmte Richtlinien, um diesen Standard festzulegen, aber nur eine Handvoll Anbieter sind für die Durchführung solcher Vorgänge zertifiziert.

Daher würde ich empfehlen, dass die beste Antwort für Sie darin besteht, die Tabelle beispielsweise in eine CSV-Datei zu exportieren und an einen fähigen Listenprozessor zu senden. Eines davon ist das SmartyStreets Bulk Address Validation Tool, das es in wenigen Sekunden bis zu einigen Minuten automatisch für Sie erledigt. Doppelte Zeilen werden mit einem neuen Feld namens "Duplizieren" und einem Wert von gekennzeichnet Y.


6
+1 für das Erkennen der Schwierigkeit beim Abgleichen von Adresszeichenfolgen, obwohl Sie möglicherweise angeben möchten, dass die Frage "Doppelte Datensätze" des OP an sich nicht komplex ist, sondern beim Vergleichen von Adressen
Geschichte

13

Eine andere Lösung wäre die Verwendung von Tabellenaliasnamen wie folgt:

SELECT p1.id, p2.id, p1.address
FROM list AS p1, list AS p2
WHERE p1.address = p2.address
AND p1.id != p2.id

Alles , was Sie wirklich in diesem Fall tun wird , um die Original - Aufnahme Liste Tabelle, die Schaffung von zwei p retend Tabellen - p 1 und p 2 - aus , dass, und dann eine Verknüpfung auf der Adress - Spalte (Zeile 3) durchgeführt wird . In der 4. Zeile wird sichergestellt, dass derselbe Datensatz in Ihren Ergebnissen nicht mehrmals angezeigt wird ("doppelte Duplikate").


1
Funktioniert gut. Wenn das WHERE mit LIKE überprüft, werden auch Apostrophe gefunden. Verlangsamt die Abfrage, aber in meinem Fall handelt es sich um einen One-Timer.
Gossi

10

Nicht sehr effizient, aber es sollte funktionieren:

SELECT *
FROM list AS outer
WHERE (SELECT COUNT(*)
        FROM list AS inner
        WHERE inner.address = outer.address) > 1;

10

Dadurch werden Duplikate in einem Tabellendurchlauf ausgewählt, keine Unterabfragen.

SELECT  *
FROM    (
        SELECT  ao.*, (@r := @r + 1) AS rn
        FROM    (
                SELECT  @_address := 'N'
                ) vars,
                (
                SELECT  *
                FROM
                        list a
                ORDER BY
                        address, id
                ) ao
        WHERE   CASE WHEN @_address <> address THEN @r := 0 ELSE 0 END IS NOT NULL
                AND (@_address := address ) IS NOT NULL
        ) aoo
WHERE   rn > 1

Diese Abfrage emuliert aktiv das ROW_NUMBER()Vorhandensein in OracleundSQL Server

Weitere Informationen finden Sie im Artikel in meinem Blog:


20
Nicht zu picken, aber FROM (SELECT ...) aooist eine Unterabfrage :-P
Rocket Hazmat

8

Dies zeigt Ihnen auch, wie viele Duplikate vorhanden sind, und ordnet die Ergebnisse ohne Verknüpfungen

SELECT  `Language` , id, COUNT( id ) AS how_many
FROM  `languages` 
GROUP BY  `Language` 
HAVING how_many >=2
ORDER BY how_many DESC

Perfekt, weil immer noch steht, wie viele Einträge dupliziert wurden
denis

4
 SELECT firstname, lastname, address FROM list
 WHERE 
 Address in 
 (SELECT address FROM list
 GROUP BY address
 HAVING count(*) > 1)

Versuchte dies auch, scheint aber nur zu hängen. Glauben Sie, dass die Rückgabe der inneren Abfrage das IN-Parameterformat nicht erfüllt.
DoubleJosh

Was meinst du damit, dass das In-Parameter-Format nicht erfüllt wird? Alles, was IN benötigt, ist, dass Ihre Unterabfrage eine einzelne Spalte zurückgeben muss. Es ist wirklich ziemlich einfach. Es ist wahrscheinlicher, dass Ihre Unterabfrage in einer Spalte generiert wird, die nicht indiziert ist, sodass die Ausführung übermäßig viel Zeit in Anspruch nimmt. Ich würde vorschlagen, wenn es lange dauert, es in zwei Abfragen zu unterteilen. Nehmen Sie die Unterabfrage, führen Sie sie zuerst in eine temporäre Tabelle aus, erstellen Sie einen Index dafür und führen Sie dann die vollständige Abfrage aus, indem Sie die Unterabfrage ausführen, in der sich Ihr doppeltes Feld in der temporären Tabelle befindet.
Ryan Roper

Ich war besorgt, dass IN eine durch Kommas getrennte Liste anstelle einer Spalte benötigte, was einfach falsch war. Hier ist die Abfrage, die für mich funktioniert hat:SELECT users.name, users.uid, users.mail, from_unixtime(created) FROM users INNER JOIN ( SELECT mail FROM users GROUP BY mail HAVING count(mail) > 1 ) dup ON users.mail = dup.mail ORDER BY users.mail, users.created;
DoubleJosh

4
select * from table_name t1 inner join (select distinct <attribute list> from table_name as temp)t2 where t1.attribute_name = t2.attribute_name

Für Ihren Tisch wäre es so etwas wie

select * from list l1 inner join (select distinct address from list as list2)l2 where l1.address=l2.address

Diese Abfrage gibt Ihnen alle eindeutigen Adresseinträge in Ihrer Listentabelle ... Ich bin nicht sicher, wie dies funktionieren wird, wenn Sie Primärschlüsselwerte für Name usw. haben.


4

Schnellstes Verfahren zum Entfernen von Duplikaten:

/* create temp table with one primary column id */
INSERT INTO temp(id) SELECT MIN(id) FROM list GROUP BY (isbn) HAVING COUNT(*)>1;
DELETE FROM list WHERE id IN (SELECT id FROM temp);
DELETE FROM temp;

2
Dies löscht offensichtlich nur den ersten Datensatz aus jeder Gruppe von Duplikaten.
Palec

4

Persönlich hat diese Abfrage mein Problem gelöst:

SELECT `SUB_ID`, COUNT(SRV_KW_ID) as subscriptions FROM `SUB_SUBSCR` group by SUB_ID, SRV_KW_ID HAVING subscriptions > 1;

In diesem Skript werden alle mehr als einmal vorhandenen Abonnenten-IDs in der Tabelle und die Anzahl der gefundenen Duplikate angezeigt.

Dies sind die Tabellenspalten:

| SUB_SUBSCR_ID | int(11)     | NO   | PRI | NULL    | auto_increment |
| MSI_ALIAS     | varchar(64) | YES  | UNI | NULL    |                |
| SUB_ID        | int(11)     | NO   | MUL | NULL    |                |    
| SRV_KW_ID     | int(11)     | NO   | MUL | NULL    |                |

Hoffe, es wird auch für Sie hilfreich sein!


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

Ersetzen Sie die Stadt durch Ihren Tisch. Ersetzen Sie den Namen durch Ihren Feldnamen


2
    SELECT *
    FROM (SELECT  address, COUNT(id) AS cnt
    FROM list
    GROUP BY address
    HAVING ( COUNT(id) > 1 ))

0
    Find duplicate Records:

    Suppose we have table : Student 
    student_id int
    student_name varchar
    Records:
    +------------+---------------------+
    | student_id | student_name        |
    +------------+---------------------+
    |        101 | usman               |
    |        101 | usman               |
    |        101 | usman               |
    |        102 | usmanyaqoob         |
    |        103 | muhammadusmanyaqoob |
    |        103 | muhammadusmanyaqoob |
    +------------+---------------------+

    Now we want to see duplicate records
    Use this query:


   select student_name,student_id ,count(*) c from student group by student_id,student_name having c>1;

+--------------------+------------+---+
| student_name        | student_id | c |
+---------------------+------------+---+
| usman               |        101 | 3 |
| muhammadusmanyaqoob |        103 | 2 |
+---------------------+------------+---+

0

Um die doppelten Zeilen schnell zu sehen, können Sie eine einzelne einfache Abfrage ausführen

Hier frage ich die Tabelle ab und liste alle doppelten Zeilen mit derselben Benutzer-ID, demselben Marktplatz und derselben SKU auf:

select user_id, market_place,sku, count(id)as totals from sku_analytics group by user_id, market_place,sku having count(id)>1;

Um die doppelte Zeile zu löschen, müssen Sie entscheiden, welche Zeile Sie löschen möchten. ZB die mit niedrigerer ID (normalerweise älter) oder andere Datumsinformationen. In meinem Fall möchte ich nur die niedrigere ID löschen, da die neuere ID die neuesten Informationen enthält.

Überprüfen Sie zunächst, ob die richtigen Datensätze gelöscht werden. Hier wähle ich den Datensatz unter den Duplikaten aus, die gelöscht werden (nach eindeutiger ID).

select a.user_id, a.market_place,a.sku from sku_analytics a inner join sku_analytics b where a.id< b.id and a.user_id= b.user_id and a.market_place= b.market_place and a.sku = b.sku;

Dann führe ich die Löschabfrage aus, um die Dupes zu löschen:

delete a from sku_analytics a inner join sku_analytics b where a.id< b.id and a.user_id= b.user_id and a.market_place= b.market_place and a.sku = b.sku;

Backup, Double Check, Verify, Verify Backup und dann ausführen.


-1

select address from list where address = any (select address from (select address, count(id) cnt from list group by address having cnt > 1 ) as t1) order by address

Die innere Unterabfrage gibt Zeilen mit doppelter Adresse zurück, während die äußere Unterabfrage die Adressspalte für Adresse mit doppelten Adressen zurückgibt. Die äußere Unterabfrage darf nur eine Spalte zurückgeben, da sie als Operand für den Operator '= any' verwendet wird.


-1

Die Antwort von Powerlord ist in der Tat die beste und ich würde eine weitere Änderung empfehlen: Verwenden Sie LIMIT, um sicherzustellen, dass db nicht überlastet wird:

SELECT firstname, lastname, list.address FROM list
INNER JOIN (SELECT address FROM list
GROUP BY address HAVING count(id) > 1) dup ON list.address = dup.address
LIMIT 10

Es ist eine gute Angewohnheit, LIMIT zu verwenden, wenn es kein WO gibt und wenn Verknüpfungen hergestellt werden. Beginnen Sie mit einem kleinen Wert, überprüfen Sie, wie schwer die Abfrage ist, und erhöhen Sie dann das Limit.


Wie trägt dies etwas zu etwas bei?
Kennet Celeste
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.