So finden Sie Duplikate in 2 Spalten, nicht in 1

107

Ich habe eine MySQL-Datenbanktabelle mit zwei Spalten, die mich interessieren. Individuell können sie jeweils Duplikate haben, aber sie sollten niemals ein Duplikat von BEIDEN mit demselben Wert haben.

stone_idkann Duplikate haben, solange für jeden upshargeTitel anders und umgekehrt. Aber sagen Sie zum Beispiel stone_id= 412 und upcharge_title= "Saphir", dass die Kombination nur einmal vorkommen sollte.

Das ist in Ordnung:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "ruby"

Das ist NICHT in Ordnung:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "sapphire"

Gibt es eine Abfrage, die in beiden Feldern Duplikate findet? Und wenn möglich, gibt es eine Möglichkeit, meine Datenbank so einzustellen, dass dies nicht möglich ist?

Ich verwende MySQL Version 4.1.22

JD Isaacks
quelle

Antworten:

192

Sie sollten einen zusammengesetzten Schlüssel zwischen den beiden Feldern einrichten. Dies erfordert eine eindeutige stone_id und einen upcharge_title für jede Zeile.

Versuchen Sie Folgendes, um die vorhandenen Duplikate zu finden:

select   stone_id,
         upcharge_title,
         count(*)
from     your_table
group by stone_id,
         upcharge_title
having   count(*) > 1
Miyagi Coder
quelle
Danke, das wählt sie aus. Könnten Sie mir so freundlich sein, mir zu sagen, wie man Duplikate löscht (aber natürlich 1 Kopie hinterlassen)? DANKE !!
JD Isaacks
2
Eine Möglichkeit wäre, alle unterschiedlichen Daten zu erfassen und die Tabelle neu zu erstellen.
Miyagi Coder
1
@ John Isaacks: Wenn es keine anderen Felder gibt, mit denen Sie sie unterscheiden können (dh alle Felder sind Duplikate), müssen Sie beide Zeilen löschen und eines neu erstellen. Eine Möglichkeit wäre, Duplikate in eine Kopie der Tabelle zu kopieren, sie aus dem Original zu löschen und bestimmte Zeilen aus der Kopie erneut einzufügen.
P Daddy
Dies funktioniert bei Postgres 8.1 nicht. Könnte mir jemand dabei helfen?
Lennon
Vielen Dank, spielt die Reihenfolge, in der Sie gruppieren, eine Rolle?
Andrew
35

Ich fand es hilfreich, einen eindeutigen Index mit einem "ALTER IGNORE" hinzuzufügen, der die Duplikate entfernt und eindeutige Datensätze erzwingt, wie Sie es gerne tun würden. Die Syntax wäre also:

ALTER IGNORE TABLE `table` ADD UNIQUE INDEX(`id`, `another_id`, `one_more_id`);

Dies fügt effektiv die eindeutige Einschränkung hinzu, was bedeutet, dass Sie niemals doppelte Datensätze haben und der IGNORE die vorhandenen doppelten Datensätze löscht.

Weitere Informationen zu eh ALTER IGNORE finden Sie hier: http://mediakey.dk/~cc/mysql-remove-duplicate-entries/

Update: Ich wurde von @Inquisitive darüber informiert, dass dies in Versionen von MySql> 5.5 fehlschlagen kann:

Unter MySQL> 5.5 und in der InnoDB-Tabelle sowie in Percona schlägt dies aufgrund der Funktion zur schnellen Indexerstellung von InnoDB [ http://bugs.mysql.com/bug.php?id=40344] fehl . In diesem Fall zuerst ausführen set session old_alter_table=1und dann funktioniert der obige Befehl einwandfrei

Update - ALTER IGNOREIn 5.7 entfernt

Aus den Dokumenten

Ab MySQL 5.6.17 ist die IGNORE-Klausel veraltet und ihre Verwendung generiert eine Warnung. IGNORE wird in MySQL 5.7 entfernt.

Einer der MySQL-Entwickler bietet zwei Alternativen :

  • Nach eindeutigen Feldern gruppieren und wie oben gezeigt löschen
  • Erstellen Sie eine neue Tabelle, fügen Sie einen eindeutigen Index hinzu, verwenden Sie z INSERT IGNORE.
CREATE TABLE duplicate_row_table LIKE regular_row_table;
ALTER TABLE duplicate_row_table ADD UNIQUE INDEX (id, another_id);
INSERT IGNORE INTO duplicate_row_table SELECT * FROM regular_row_table;
DROP TABLE regular_row_table;
RENAME TABLE duplicate_row_table TO regular_row_table;

Abhängig von der Größe Ihres Tisches ist dies jedoch möglicherweise nicht praktikabel

SeanDowney
quelle
1
Stimmt, aber zumindest für das nächste Mal weißt du es. Ich hatte das gleiche Problem und fand es gut, es mit anderen zu teilen
SeanDowney
Ich habe nur geärgert, dass es 3 Jahre zu spät ist. Bin wirklich froh, dass du geteilt hast. Daher das Plus 1.
JD Isaacks
Ich stelle mir vor, dass dadurch eines der Duplikate willkürlich entfernt wird. Stellen Sie daher sicher, dass zwischen den einzelnen Zeilen keine unterschiedlichen Daten vorhanden sind, deren Kenntnis oder Aufbewahrung hilfreich sein könnte.
Joshua Pinter
+1 für die Antwort auch nach 2 Jahren Verspätung. Ich habe versehentlich einen zusammengesetzten Schlüssel gelöscht und dies war ein Lebensretter. Vielen Dank
ivcode
Ich habe einige Duplizierungsfindertechniken ausprobiert und keine davon war so einfach und schnell. Vielen Dank, dass Sie diese Methode geteilt haben.
Kristjan O.
8

Sie können Duplikate wie dieses finden ..

Select
    stone_id, upcharge_title, count(*)
from 
    particulartable
group by 
    stone_id, upcharge_title
having 
    count(*) > 1
Jason Punyon
quelle
4

So finden Sie die Duplikate:

select stone_id, upcharge_title from tablename group by stone_id, upcharge_title having count(*)>1

Um dies in Zukunft zu vermeiden, erstellen Sie einen zusammengesetzten eindeutigen Schlüssel für diese beiden Felder.

Ian Nelson
quelle
1
Vielen Dank. Können Sie mir bitte sagen, wie ich alle Duplikate bis auf eines löschen soll? Und wie richte ich einen Compisite-Schlüssel in phpmyadmin ein? DANKE!!!
JD Isaacks
3

Im Übrigen würde eine zusammengesetzte eindeutige Einschränkung für die Tabelle verhindern, dass dies überhaupt auftritt.

ALTER TABLE table
    ADD UNIQUE(stone_id, charge_title)

(Dies ist gültiges T-SQL. Ich bin mir bei MySQL nicht sicher.)

P Papa
quelle
1
Ich denke, das funktioniert, aber ich werde es nicht tun, bis ich zuerst die Duplikate entferne. Vielen Dank.
JD Isaacks
1

Dieser SO-Beitrag hat mir geholfen, aber ich wollte auch wissen, wie man eine der Zeilen löscht und behält ... hier ist eine PHP-Lösung, um die doppelten Zeilen zu löschen und eine zu behalten (in meinem Fall gab es nur 2 Spalten und sie befindet sich in einer Funktion zum Löschen doppelter Kategoriezuordnungen)

$dupes = $db->query('select *, count(*) as NUM_DUPES from PRODUCT_CATEGORY_PRODUCT group by fkPRODUCT_CATEGORY_ID, fkPRODUCT_ID having count(*) > 1');
if (!is_array($dupes))
    return true;
foreach ($dupes as $dupe) {
    $db->query('delete from PRODUCT_CATEGORY_PRODUCT where fkPRODUCT_ID = ' . $dupe['fkPRODUCT_ID'] . ' and fkPRODUCT_CATEGORY_ID = ' . $dupe['fkPRODUCT_CATEGORY_ID'] . ' limit ' . ($dupe['NUM_DUPES'] - 1);
}

Das (Limit NUM_DUPES - 1) bewahrt die einzelne Zeile ...

Danke an alle

Groovenectar
quelle
3
ALTER IGNORE TABLE table ADD UNIQUE INDEX index_name(stone_id, charge_title)entfernt doppelte Zeilen und hinterlässt nur ein eindeutiges Paar.
Dev-Null-Bewohner