Ich habe eine Tabelle mit folgenden Feldern:
id (Unique)
url (Unique)
title
company
site_id
Jetzt muss ich Zeilen mit demselben title, company and site_id
entfernen. Eine Möglichkeit, dies zu tun, wird die folgende SQL mit einem Skript (PHP
) verwenden:
SELECT title, site_id, location, id, count( * )
FROM jobs
GROUP BY site_id, company, title, location
HAVING count( * ) >1
Nach dem Ausführen dieser Abfrage kann ich Duplikate mithilfe eines serverseitigen Skripts entfernen.
Ich möchte jedoch wissen, ob dies nur mit SQL-Abfragen möglich ist.
Ein wirklich einfacher Weg, dies zu tun, ist das Hinzufügen eines UNIQUE
-Index für die 3 Spalten. Wenn Sie die Anweisung ALTER
schreiben, schließen Sie das Schlüsselwort IGNORE
ein. So wie:
ALTER IGNORE TABLE jobs
ADD UNIQUE INDEX idx_name (site_id, title, company);
Dadurch werden alle doppelten Zeilen gelöscht. Als zusätzlicher Vorteil werden zukünftige INSERTs
, die Duplikate sind, fehlerhaft ausgegeben. Wie immer möchten Sie vielleicht ein Backup machen, bevor Sie so etwas ausführen ...
Wenn Sie die Spalteneigenschaften nicht ändern möchten, können Sie die folgende Abfrage verwenden.
Da Sie über eine Spalte mit eindeutigen IDs verfügen (z. B. auto_increment
-Spalten), können Sie sie zum Entfernen der Duplikate verwenden:
DELETE `a`
FROM
`jobs` AS `a`,
`jobs` AS `b`
WHERE
-- IMPORTANT: Ensures one version remains
-- Change "ID" to your unique column's name
`a`.`ID` < `b`.`ID`
-- Any duplicates you want to check for
AND (`a`.`title` = `b`.`title` OR `a`.`title` IS NULL AND `b`.`title` IS NULL)
AND (`a`.`company` = `b`.`company` OR `a`.`company` IS NULL AND `b`.`company` IS NULL)
AND (`a`.`site_id` = `b`.`site_id` OR `a`.`site_id` IS NULL AND `b`.`site_id` IS NULL);
In MySQL können Sie es mit dem NULL-sicheren Gleichheitsoperator (aka "Spaceship Operator" ) noch weiter vereinfachen:
DELETE `a`
FROM
`jobs` AS `a`,
`jobs` AS `b`
WHERE
-- IMPORTANT: Ensures one version remains
-- Change "ID" to your unique column's name
`a`.`ID` < `b`.`ID`
-- Any duplicates you want to check for
AND `a`.`title` <=> `b`.`title`
AND `a`.`company` <=> `b`.`company`
AND `a`.`site_id` <=> `b`.`site_id`;
MySQL hat Einschränkungen hinsichtlich der Bezugnahme auf die Tabelle, aus der Sie löschen. Sie können dies mit einer temporären Tabelle umgehen, z.
create temporary table tmpTable (id int);
insert tmpTable
(id)
select id
from YourTable yt
where exists
(
select *
from YourTabe yt2
where yt2.title = yt.title
and yt2.company = yt.company
and yt2.site_id = yt.site_id
and yt2.id > yt.id
);
delete
from YourTable
where ID in (select id from tmpTable);
Aus Kostanos 'Vorschlag in den Kommentaren:
. Die einzige langsame Abfrage oben ist DELETE, wenn Sie eine sehr große Datenbank haben. Diese Abfrage könnte schneller sein:
DELETE FROM YourTable USING YourTable, tmpTable WHERE YourTable.id=tmpTable.id
Wenn die IGNORE
-Anweisung nicht wie in meinem Fall funktioniert, können Sie die folgende Anweisung verwenden:
CREATE TABLE your_table_deduped like your_table;
INSERT your_table_deduped SELECT * FROM your_table GROUP BY index1_id, index2_id;
RENAME TABLE your_table TO your_table_with_dupes;
RENAME TABLE your_table_deduped TO your_table;
#OPTIONAL
ALTER TABLE `your_table` ADD UNIQUE `unique_index` (`index1_id`, `index2_id`);
#OPTIONAL
DROP TABLE your_table_with_dupes;
Es gibt eine andere Lösung:
DELETE t1 FROM my_table t1, my_table t2 WHERE t1.id < t2.id AND t1.my_field = t2.my_field AND t1.my_field_2 = t2.my_field_2 AND ...
Das Löschen von Duplikaten in MySQL-Tabellen ist ein häufiges Problem. Normalerweise ist dies das Ergebnis einer fehlenden Einschränkung, um diese Duplikate zu vermeiden. Diese häufige Problematik bringt jedoch meist spezifische Bedürfnisse mit sich, die spezifische Ansätze erfordern. Der Ansatz sollte unterschiedlich sein, z. B. abhängig von der Größe der Daten, von dem doppelten Eintrag, der aufbewahrt werden sollte (in der Regel der erste oder der letzte), ob es Indizes gibt, die aufbewahrt werden müssen, oder ob wir einen zusätzlichen Eintrag vornehmen möchten Aktion für die duplizierten Daten.
Es gibt auch einige Besonderheiten in MySQL selbst, z. B. dass Sie nicht die gleiche Tabelle auf eine FROM-Ursache verweisen können, wenn Sie eine Tabelle UPDATE ausführen (dies führt zu MySQL-Fehler # 1093). Diese Einschränkung kann überwunden werden, indem eine innere Abfrage mit einer temporären Tabelle verwendet wird (wie in einigen Ansätzen oben vorgeschlagen). Diese innere Abfrage funktioniert jedoch nicht besonders gut, wenn mit großen Datenquellen gearbeitet wird.
Es gibt jedoch einen besseren Ansatz zum Entfernen von Duplikaten, der sowohl effizient als auch zuverlässig ist und leicht an unterschiedliche Bedürfnisse angepasst werden kann.
Die allgemeine Idee ist, eine neue temporäre Tabelle zu erstellen, die in der Regel eine eindeutige Einschränkung hinzufügt, um weitere Duplikate zu vermeiden, und die Daten aus Ihrer früheren Tabelle in die neue einzufügen, während sie sich um die Duplikate kümmern. Dieser Ansatz basiert auf einfachen MySQL-INSERT-Abfragen, erstellt eine neue Einschränkung, um weitere Duplikate zu vermeiden, und überspringt die Notwendigkeit, eine innere Abfrage zu verwenden, um nach Duplikaten zu suchen, und einer temporären Tabelle, die im Speicher aufbewahrt werden sollte (und somit auch große Datenquellen passt).
So kann es erreicht werden. Dazu haben wir eine Tabelle employee mit den folgenden Spalten:
employee (id, first_name, last_name, start_date, ssn)
Um die Zeilen mit einer doppelten Spalte ssn zu löschen und nur den ersten gefundenen Eintrag beizubehalten, kann wie folgt vorgegangen werden:
-- create a new tmp_eployee table
CREATE TABLE tmp_employee LIKE employee;
-- add a unique constraint
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
-- scan over the employee table to insert employee entries
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id;
-- rename tables
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
⇒ Bei diesem Ansatz wurden 1.6M-Register in weniger als 200s in 6k umgewandelt.
Chetan Nach diesem Vorgang können Sie schnell und einfach alle Ihre Duplikate entfernen und eine UNIQUE-Einschränkung erstellen, indem Sie Folgendes ausführen:
CREATE TABLE tmp_jobs LIKE jobs;
ALTER TABLE tmp_jobs ADD UNIQUE(site_id, title, company);
INSERT IGNORE INTO tmp_jobs SELECT * FROM jobs ORDER BY id;
RENAME TABLE jobs TO backup_jobs, tmp_jobs TO jobs;
Natürlich kann dieser Prozess weiter modifiziert werden, um ihn beim Löschen von Duplikaten an unterschiedliche Anforderungen anzupassen. Einige Beispiele folgen.
Manchmal müssen wir den letzten duplizierten Eintrag anstelle des ersten aufbewahren.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id DESC;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
Manchmal müssen wir die gefundenen doppelten Einträge weiter bearbeiten (z. B. das Zählen der Duplikate).
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
Manchmal verwenden wir ein Auto-Incremental-Feld. Um den Index so kompakt wie möglich zu halten, können wir die Löschung der Duplikate nutzen, um das Auto-Incremental-Feld in der neuen temporären Tabelle neu zu generieren.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT (first_name, last_name, start_date, ssn) FROM employee ORDER BY id;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
Je nach gewünschtem Verhalten sind auch viele weitere Modifikationen möglich. Als Beispiel verwenden die folgenden Abfragen eine zweite temporäre Tabelle, um neben 1) den letzten Eintrag anstelle des ersten zu behalten; und 2) Erhöhen eines Zählers auf den gefundenen Duplikaten; auch 3) regeneriere die automatisch inkrementelle Feld-ID, wobei die Eingabereihenfolge wie in den vorherigen Daten beibehalten wird.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id DESC ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
CREATE TABLE tmp_employee2 LIKE tmp_employee;
INSERT INTO tmp_employee2 SELECT (first_name, last_name, start_date, ssn) FROM tmp_employee ORDER BY id;
DROP TABLE tmp_employee;
RENAME TABLE employee TO backup_employee, tmp_employee2 TO employee;
Ich habe dieses Abfrage-Snipet für SQL Server, aber ich denke, es kann in anderen DBMS mit kleinen Änderungen verwendet werden:
DELETE
FROM Table
WHERE Table.idTable IN (
SELECT MAX(idTable)
FROM idTable
GROUP BY field1, field2, field3
HAVING COUNT(*) > 1)
Ich habe vergessen zu sagen, dass diese Abfrage die Zeile mit der niedrigsten ID der duplizierten Zeilen nicht entfernt. Wenn dies für Sie funktioniert, versuchen Sie diese Abfrage:
DELETE
FROM jobs
WHERE jobs.id IN (
SELECT MAX(id)
FROM jobs
GROUP BY site_id, company, title, location
HAVING COUNT(*) > 1)
Einfach und schnell für alle Fälle:
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_duplicates AS (SELECT dub.id FROM table_with_duplications dub GROUP BY dub.field_must_be_uniq_1, dub.field_must_be_uniq_2 HAVING COUNT(*) > 1);
DELETE FROM table_with_duplications WHERE id IN (SELECT id FROM _temp_duplicates);
Der schnellere Weg ist das Einfügen unterschiedlicher Zeilen in eine temporäre Tabelle. Mit delete habe ich einige Stunden gebraucht, um Duplikate aus einer Tabelle mit 8 Millionen Zeilen zu entfernen. Mit Einsatz und Unterscheidung dauerte es nur 13 Minuten.
CREATE TABLE tempTableName LIKE tableName;
CREATE INDEX ix_all_id ON tableName(cellId,attributeId,entityRowId,value);
INSERT INTO tempTableName(cellId,attributeId,entityRowId,value) SELECT DISTINCT cellId,attributeId,entityRowId,value FROM tableName;
TRUNCATE TABLE tableName;
INSERT INTO tableName SELECT * FROM tempTableName;
DROP TABLE tempTableName;
Ich besuche diese Seite jedes Mal, wenn ich google "entferne Duplikate aus MySQL", aber für meine theIGNORE-Lösungen funktioniert das nicht, weil ich InnoDB-MySQL-Tabellen habe
dieser Code funktioniert jederzeit besser
CREATE TABLE tableToclean_temp LIKE tableToclean;
ALTER TABLE tableToclean_temp ADD UNIQUE INDEX (fontsinuse_id);
INSERT IGNORE INTO tableToclean_temp SELECT * FROM tableToclean;
DROP TABLE tableToclean;
RENAME TABLE tableToclean_temp TO tableToclean;
tableToclean = der Name der zu reinigenden Tabelle
tableToclean_temp = Eine temporäre Tabelle erstellt und gelöscht
Eine Lösung, die einfach zu verstehen ist und ohne Primärschlüssel funktioniert:
1) füge eine neue boolesche Spalte hinzu
alter table mytable add tokeep boolean;
2) Fügen Sie eine Einschränkung für die duplizierten Spalten UND die neue Spalte hinzu
alter table mytable add constraint preventdupe unique (mycol1, mycol2, tokeep);
3) Setzen Sie die boolesche Spalte auf true. Dies wird aufgrund der neuen Einschränkung nur in einer der duplizierten Zeilen gelingen
update ignore mytable set tokeep = true;
4) Löschen Sie Zeilen, die nicht als Tokeep markiert wurden
delete from mytable where tokeep is null;
5) lassen Sie die hinzugefügte Säule fallen
alter table mytable drop tokeep;
Ich empfehle Ihnen, die von Ihnen hinzugefügte Einschränkung beizubehalten, damit in Zukunft neue Duplikate vermieden werden.
Diese Lösung wird die Duplikate in eine Tabelle verschieben und die Uniques in eine andere .
-- speed up creating uniques table if dealing with many rows
CREATE INDEX temp_idx ON jobs(site_id, company, title, location);
-- create the table with unique rows
INSERT jobs_uniques SELECT * FROM
(
SELECT *
FROM jobs
GROUP BY site_id, company, title, location
HAVING count(1) > 1
UNION
SELECT *
FROM jobs
GROUP BY site_id, company, title, location
HAVING count(1) = 1
) x
-- create the table with duplicate rows
INSERT jobs_dupes
SELECT *
FROM jobs
WHERE id NOT IN
(SELECT id FROM jobs_uniques)
-- confirm the difference between uniques and dupes tables
SELECT COUNT(1)
AS jobs,
(SELECT COUNT(1) FROM jobs_dupes) + (SELECT COUNT(1) FROM jobs_uniques)
AS sum
FROM jobs
wenn Sie eine große Tabelle mit einer großen Anzahl von Datensätzen haben, funktionieren die oben genannten Lösungen nicht oder dauern zu lange. Dann haben wir eine andere Lösung
-- Create temporary table
CREATE TABLE temp_table LIKE table1;
-- Add constraint
ALTER TABLE temp_table ADD UNIQUE(title, company,site_id);
-- Copy data
INSERT IGNORE INTO temp_table SELECT * FROM table1;
-- Rename and drop
RENAME TABLE table1 TO old_table1, temp_table TO table1;
DROP TABLE old_table1;
Löschen Sie doppelte Zeilen mit der DELETE JOIN-Anweisung MySQL stellt Ihnen die DELETE JOIN-Anweisung zur Verfügung, mit der Sie doppelte Zeilen schnell entfernen können.
Die folgende Anweisung löscht doppelte Zeilen und behält die höchste ID bei:
DELETE t1 FROM contacts t1
INNER JOIN
contacts t2 WHERE
t1.id < t2.id AND t1.email = t2.email;
Ich habe einen einfachen Weg gefunden. (Bleib auf dem Laufenden)
DELETE t1 FROM tablename t1 INNER JOIN tablename t2
WHERE t1.id < t2.id AND t1.column1 = t2.column1 AND t1.column2 = t2.column2;
Ab Version 8.0 (2018) unterstützt MySQL schließlich window-Funktionen .
Fensterfunktionen sind sowohl praktisch als auch effizient. Hier ist eine Lösung, die zeigt, wie Sie diese Aufgabe lösen können.
In einer Unterabfrage können wir ROW_NUMBER()
verwenden, um jedem Datensatz in der Tabelle innerhalb von column1/column2
-Gruppen eine Position zuzuweisen, sortiert nach id
. Wenn keine Duplikate vorhanden sind, erhält der Datensatz die Zeilennummer 1
. Wenn Duplikate vorhanden sind, werden sie durch aufsteigende id
(beginnend mit 1
) nummeriert.
Sobald Datensätze in der Unterabfrage ordnungsgemäß nummeriert sind, werden durch die äußere Abfrage alle Datensätze gelöscht, deren Zeilennummer nicht 1 ist.
Abfrage:
DELETE FROM tablename
WHERE id IN (
SELECT id
FROM (
SELECT
id,
ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id) rn
FROM output
) t
WHERE rn > 1
)
Um Datensätze mit eindeutigen Spalten zu duplizieren, z. COL1, COL2, COL3 sollten nicht repliziert werden. (Angenommen, wir haben 3 Spalten übersehen, die in der Tabellenstruktur eindeutig sind, und es wurden mehrere doppelte Einträge in die Tabelle vorgenommen.)
DROP TABLE TABLE_NAME_copy;
CREATE TABLE TABLE_NAME_copy LIKE TABLE_NAME;
INSERT INTO TABLE_NAME_copy
SELECT * FROM TABLE_NAME
GROUP BY COLUMN1, COLUMN2, COLUMN3;
DROP TABLE TABLE_NAME;
ALTER TABLE TABLE_NAME_copy RENAME TO TABLE_NAME;
Hoffnung hilft dev.
Ein ausführlich beschriebenes Tutorial zur Lösung dieses Problems finden Sie auf der mysqltutorial.org Site:
So löschen Sie doppelte Zeilen in MySQL
Es wird sehr deutlich gezeigt, wie doppelte Zeilen auf drei verschiedene Arten gelöscht werden :
A) Using DELETE JOIN
statement
B) Verwenden einer Zwischentabelle
C) Verwenden der Funktion ROW_NUMBER()
Ich hoffe, dass es jemandem hilft.
So löschen Sie den doppelten Datensatz in einer Tabelle.
delete from job s
where rowid < any
(select rowid from job k
where s.site_id = k.site_id and
s.title = k.title and
s.company = k.company);
oder
delete from job s
where rowid not in
(select max(rowid) from job k
where s.site_id = k.site_id and
s.title = k.title and
s.company = k.company);
-- Here is what I used, and it works:
create table temp_table like my_table;
-- t_id is my unique column
insert into temp_table (id) select id from my_table GROUP by t_id;
delete from my_table where id not in (select id from temp_table);
drop table temp_table;