Удаление дубликатов в таблицах MySQL является распространенной проблемой, обычно это результат отсутствия ограничения, позволяющего избежать этих дубликатов заранее. Но эта общая проблема обычно связана с конкретными потребностями ... которые требуют определенных подходов. Подход должен отличаться в зависимости, например, от размера данных, дублируемой записи, которая должна быть сохранена (обычно первая или последняя), от того, есть ли индексы, которые нужно сохранить, или от того, хотим ли мы выполнить какие-либо дополнительные действия. действие на дублированные данные.
Есть также некоторые особенности самого MySQL, такие как невозможность ссылаться на одну и ту же таблицу по причине FROM при выполнении таблицы UPDATE (это вызовет ошибку MySQL # 1093). Это ограничение можно преодолеть, используя внутренний запрос с временной таблицей (как предложено в некоторых подходах выше). Но этот внутренний запрос не будет работать особенно хорошо при работе с большими источниками данных.
Тем не менее, существует лучший подход для удаления дубликатов, он эффективен и надежен, и его можно легко адаптировать к различным потребностям.
Общая идея состоит в том, чтобы создать новую временную таблицу, обычно добавляя уникальное ограничение, чтобы избежать дальнейших дубликатов, и вставлять данные из прежней таблицы в новую, одновременно заботясь о дубликатах. Этот подход основан на простых запросах MySQL INSERT, создает новое ограничение, чтобы избежать дальнейших дубликатов, и пропускает необходимость использования внутреннего запроса для поиска дубликатов и временной таблицы, которая должна храниться в памяти (таким образом, подходя также для больших источников данных).
Вот как это может быть достигнуто. Учитывая, что у нас есть таблица employee , со следующими столбцами:
employee (id, first_name, last_name, start_date, ssn)
Чтобы удалить строки с повторяющимся столбцом ssn и сохранить только первую найденную запись, можно выполнить следующий процесс:
-- 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;
Техническое объяснение
- Строка # 1 создает новую таблицу tmp_eployee с точно такой же структурой, что и у таблицы employee
- Строка # 2 добавляет уникальное ограничение к новой таблице tmp_eployee , чтобы избежать дальнейших дубликатов
- Строка # 3 просматривает исходную таблицу employee по идентификатору, вставляя новые записи сотрудников в новую таблицу tmp_eployee , игнорируя при этом дублированные записи
- Строка # 4 переименовывает таблицы, так что новая таблица employee содержит все записи без дубликатов, а резервная копия прежних данных сохраняется в таблице backup_employee
⇒ Используя этот подход, регистры 1.6M были преобразованы в 6k менее чем за 200 с.
Четан , следуя этому процессу, вы можете быстро и легко удалить все свои дубликаты и создать УНИКАЛЬНОЕ ограничение, запустив:
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;
Конечно, этот процесс может быть дополнительно изменен, чтобы адаптировать его к различным потребностям при удалении дубликатов. Вот несколько примеров.
✔ Вариант сохранения последней записи вместо первой
Иногда нам нужно сохранить последнюю дублированную запись вместо первой.
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;
- В строке # 3 предложение ORDER BY id DESC устанавливает последние идентификаторы, которые получают приоритет над остальными
✔ Вариант выполнения некоторых задач с дубликатами, например, ведение учета найденных дубликатов
Иногда нам необходимо выполнить дополнительную обработку найденных дублированных записей (например, вести учет дубликатов).
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;
- В строке # 3 создается новый столбец n_duplicates
- В строке # 4 запрос INSERT INTO ... ON DUPLICATE KEY UPDATE используется для выполнения дополнительного обновления при обнаружении дубликата (в этом случае увеличение счетчика)
Запрос INSERT INTO ... ON DUPLICATE KEY UPDATE может использоваться для выполнения различных типов обновлений для найденных дубликатов.
✔ Вариант для регенерации автоинкрементного поля id
Иногда мы используем автоинкрементное поле и, чтобы индекс был максимально компактным, мы можем воспользоваться удалением дубликатов для регенерацииавтоинкрементное поле в новой временной таблице.
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;
- В строке № 3 вместо выбора всех полей в таблице поле id пропускается, так что механизм БД генерирует новыйодин автоматически
✔ Другие варианты
Многие дополнительные модификации также возможны в зависимости от желаемого поведения.В качестве примера, следующие запросы будут использовать вторую временную таблицу, чтобы, кроме 1) сохранить последнюю запись вместо первой;и 2) увеличить счетчик найденных дубликатов;также 3) восстановить автоматически инкрементальный идентификатор поля, сохраняя порядок ввода, как это было на предыдущих данных.
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;