Hin und wieder kann es vorkommen, dass sich Datensätze ungewollt doppelt in der Datenbank wiederfinden.
Hier ein kleiner Spickzettel, wie sich die Doubletten wieder entfernen lassen.
Die Struktur der Testtabelle sieht folgendermaßen aus:
CREATE TABLE `sqltest`.`duplicates` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`a` VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`b` INT(11) NOT NULL ,
`c` DATETIME NOT NULL ,
PRIMARY KEY (`id`)
) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
Noch ein paar Daten rein und fertig ist die Spielwiese.
mysql> select * from duplicates; +----+--------+-----+---------------------+ | id | a | b | c | +----+--------+-----+---------------------+ | 1 | Apfel | 123 | 2011-01-11 17:00:00 | | 2 | Apfel | 123 | 2011-01-11 17:00:00 | | 3 | Banane | 234 | 2011-01-12 17:00:00 | | 4 | Banane | 234 | 2011-01-12 17:00:00 | | 5 | Birne | 456 | 2011-01-15 17:00:00 | | 6 | Birne | 456 | 2011-01-15 17:00:00 | +----+--------+-----+---------------------+ 6 rows in set (0.00 sec)
Die erste Abfrage macht einen JOIN auf sich selbst, dafür die Aliasse d1 und d2 und dient zunächst der Prüfung, ob wir die gewünschten Datensätze geliefert bekommen.
SELECT d1.* FROM duplicates d1, duplicates d2 WHERE d1.id != d2.id AND d1.a = d2.a AND d1.b = d2.b AND d1.c = d2.c AND d1.id < d2.id
Das Ergebnis:
+----+--------+-----+---------------------+ | id | a | b | c | +----+--------+-----+---------------------+ | 1 | Apfel | 123 | 2011-01-11 17:00:00 | | 3 | Banane | 234 | 2011-01-12 17:00:00 | | 5 | Birne | 456 | 2011-01-15 17:00:00 | +----+--------+-----+---------------------+ 3 rows in set (0.00 sec)
MySQL kann offensichtlich keine Daten löschen, wenn die Löschaktion auf derselben Tabelle wie die Subquery gemacht wird. Darum schreiben wir die Ids in eine temporäre Tabelle.
CREATE TABLE duplicates_ids AS ( SELECT d2.id FROM duplicates d1, duplicates d2 WHERE d1.id != d2.id AND d1.a = d2.a AND d1.b = d2.b AND d1.c = d2.c AND d1.id < d2.id )
Im letzten Schritt löschen wir die zuvor ausgewählten Datensätze und auch die temporäre Tabelle. Je nachdem was wir behalten wollen, noch ggf. ein “NOT” vor das “IN” schreiben.
DELETE FROM duplicates WHERE id [NOT] IN (SELECT * FROM duplicates_ids); DROP TABLE duplicates_ids;
Achtung! Ich hafte nicht bei Datenverlust oder sonstigen Fehlern. Ein Backup sollte sicherheitshalber vorher gemacht werden.
12.01.2011 um 08:26 Uhr
Gut, das haut aber nur nur dann hin, wenn es mindestens ein Unterscheidungsmerkmal zwischen den Duplikaten gibt (in deinem Fall die ID). Ich überlege grad, ob sowas (doppelte, identische Einträge) nicht direkt durch DB-interne Mechanismen im Design vermieden werden sollte (unique etc.).
12.01.2011 um 09:43 Uhr
Im besten Fall würde man das schon während der Entwicklung berücksichtigen, aber manchmal treten gewisse Dinge auch erst später auf.
Mindestens ein eindeutiges Merkmal muss schon sein, da solche Aktionen sonst noch unhandlicher würden. Aber auch dann lässt sich die Datenmenge natürlich reduzieren (kopieren mit DISTINCT z.B.).
12.01.2011 um 09:45 Uhr
Eine weiter möglichkeit wäre auf die Spalte mit doppelten Einträgen nachträglich einen unique-Key zu setzen. Mit dem Schlüsselwort IGNORE löscht MySql automatisch doppelte Einträge.
ALTER IGNORE TABLE duplicates ADD UNIQUE INDEX a
Hier sollte es auch möglich sein mit ORDER BY (oder vielleicht eine andere Methode) die Reihenfolge festzulegen. Das hab ich jetzt aber nicht getestet.
12.01.2011 um 10:42 Uhr
Das ist schön und gut, dass die Ids aber referentielle Abhängigkeiten mit anderen Tabellen haben könnten, wird hier gar nicht behandelt.
Ich hatte letztens mal wieder einen schönen vermüllten Datensatz mit Nutzerregistrierungen und den unterschiedlichsten Merkmalen.
Duplikate habe ich mit mehreren Methoden, von grob auf fein herausgesucht.. Das waren einfach mehrere Queries, die mit GROUP BY und COUNT() und HAVING COUNT() > 1 einzelne Duplikate gefunden haben. So funktioniert es auch über mehrere Tabellen hinweg mit einem JOIN.
In einer Schleife wurden dann die tatsächlichen Duplikate mit allen IDs und weiteren Merkmalen herausgesucht.
So konnte ich in späteren Datensätzen gegebenenfalls nicht mehr vorhandene Merkmale erhalten, mir die Reihenfolge aussuchen und mit einem Update auf die referentiellen Abhängigkeiten auf die neue ID hinweisen.
Da das eine einmalige Aktion ist, ist die Rechenintensität zu verschmerzen..
Pingback: doppelte Einträge in MySQL Datenbank finden « JimiHUY
18.11.2011 um 09:44 Uhr
DELETE FROM a
USING `db`.`tbl` a, `db`.`tbl` b
WHERE b.Mail = a.Mail
AND b.ID != a.ID