Как удалить дубликаты в таблице MySQL? - PullRequest
149 голосов
/ 13 апреля 2010

Мне нужно DELETE дублировать строки для указанного sid в таблице MySQL.

Как я могу сделать это с помощью SQL-запроса?

DELETE (DUPLICATED TITLES) FROM table WHERE SID = "1"

Примерно так, но я не знаю, как это сделать.

Ответы [ 23 ]

201 голосов
/ 13 апреля 2010

удаляет дубликаты на месте, без создания новой таблицы

ALTER IGNORE TABLE `table_name` ADD UNIQUE (title, SID)

примечание: хорошо работает, только если индекс помещается в память

117 голосов
/ 30 апреля 2011

Предположим, у вас есть таблица employee со следующими столбцами:

employee (first_name, last_name, start_date)

Чтобы удалить строки с повторяющимся столбцом first_name:

delete
from employee using employee,
    employee e1
where employee.id > e1.id
    and employee.first_name = e1.first_name  
54 голосов
/ 13 апреля 2010

После удаления дубликатов для всех SID, а не только одного.

С временной таблицей

CREATE TABLE table_temp AS
SELECT * FROM table GROUP BY title, SID;

DROP TABLE table;
RENAME TABLE table_temp TO table;

Поскольку temp_table только что создан, он не имеет индексов. Вам нужно будет воссоздать их после удаления дубликатов. Вы можете проверить, какие индексы у вас есть в таблице с помощью SHOW INDEXES IN table

Без временной таблицы:

DELETE FROM `table` WHERE id IN (
  SELECT all_duplicates.id FROM (
    SELECT id FROM `table` WHERE (`title`, `SID`) IN (
      SELECT `title`, `SID` FROM `table` GROUP BY `title`, `SID` having count(*) > 1
    )
  ) AS all_duplicates 
  LEFT JOIN (
    SELECT id FROM `table` GROUP BY `title`, `SID` having count(*) > 1
  ) AS grouped_duplicates 
  ON all_duplicates.id = grouped_duplicates.id 
  WHERE grouped_duplicates.id IS NULL
)
47 голосов
/ 25 августа 2014

Удаление повторяющихся строк в MySQL, пошаговое руководство

Создайте таблицу и вставьте несколько строк:

dev-db> create table penguins(foo int, bar varchar(15), baz datetime);
Query OK, 0 rows affected (0.07 sec)
dev-db> insert into penguins values(1, 'skipper', now());
dev-db> insert into penguins values(1, 'skipper', now());
dev-db> insert into penguins values(3, 'kowalski', now());
dev-db> insert into penguins values(3, 'kowalski', now());
dev-db> insert into penguins values(3, 'kowalski', now());
dev-db> insert into penguins values(4, 'rico', now());
Query OK, 6 rows affected (0.07 sec)
dev-db> select * from penguins;
+------+----------+---------------------+
| foo  | bar      | baz                 |
+------+----------+---------------------+
|    1 | skipper  | 2014-08-25 14:21:54 |
|    1 | skipper  | 2014-08-25 14:21:59 |
|    3 | kowalski | 2014-08-25 14:22:09 |
|    3 | kowalski | 2014-08-25 14:22:13 |
|    3 | kowalski | 2014-08-25 14:22:15 |
|    4 | rico     | 2014-08-25 14:22:22 |
+------+----------+---------------------+
6 rows in set (0.00 sec)

Затем удалите дубликаты:

dev-db> delete a
    -> from penguins a
    -> left join(
    -> select max(baz) maxtimestamp, foo, bar
    -> from penguins
    -> group by foo, bar) b
    -> on a.baz = maxtimestamp and
    -> a.foo = b.foo and
    -> a.bar = b.bar
    -> where b.maxtimestamp IS NULL;
Query OK, 3 rows affected (0.01 sec)

Результат:

dev-db> select * from penguins;
+------+----------+---------------------+
| foo  | bar      | baz                 |
+------+----------+---------------------+
|    1 | skipper  | 2014-08-25 14:21:59 |
|    3 | kowalski | 2014-08-25 14:22:15 |
|    4 | rico     | 2014-08-25 14:22:22 |
+------+----------+---------------------+
3 rows in set (0.00 sec)

Что делает этот оператор удаления

Псевдокод: сгруппируйте строки по двум столбцам, для которых вы хотите удалить дубликаты. Выберите одну строку каждой группы для сохранения, используя максимальный агрегат. Левое объединение возвращает все строки из левой таблицы с соответствующими строками в правой таблице. В этом случае левая таблица содержит все строки в таблице, а правая содержит только те строки, которые имеют значение NULL (не одну строку на группу, которую вы хотите сохранить). Удаляя эти строки, у вас остается только один уникальный на группу.

Более подробное техническое объяснение, как читать этот оператор удаления sql:

Настольные пингвины с псевдонимом «a» оставлены соединенными с подмножеством настольных пингвинов, которое называется «b». Правая таблица «b», которая является подмножеством, находит максимальную временную метку, сгруппированную по foo и bar. Это соответствует левой таблице «а». (foo, bar, baz) слева имеет каждую строку в таблице. Правое подмножество 'b' имеет (maxtimestamp, foo, bar), которое соответствует левому только тому, которое является максимальным.

Каждая строка, отличная от max, имеет значение maxtimestamp, равное NULL. Отфильтруйте эти строки со значением NULL, и вы получите набор всех строк, сгруппированных по foo и bar, который не является последней базой меток времени. Удалить те.

Перед запуском сделайте резервную копию таблицы.

Предотвратите повторение этой проблемы в этой таблице:

Если вы заставили это сработать, и оно потушило ваш огонь "двойных рядов". Отлично. Ваша работа еще не закончена. Определите новый составной уникальный ключ в вашей таблице (в этих двух столбцах), чтобы предотвратить добавление дополнительных дубликатов. Подобно хорошей иммунной системе, плохие строки даже не должны попадать в таблицу во время вставки. Позже все эти программы, добавляющие дубликаты, будут транслировать свои протесты, и когда вы исправите их, эта проблема больше не возникнет.

12 голосов
/ 16 мая 2016

Мне всегда кажется, что это работает:

CREATE TABLE NoDupeTable LIKE DupeTable; 
INSERT NoDupeTable SELECT * FROM DupeTable group by CommonField1,CommonFieldN;

Который хранит наименьший идентификатор для каждого из дубликатов и остальных записей, не относящихся к дублированию.

Я также сделал следующее, чтобы проблема с дублированием больше не возникала после удаления:

CREATE TABLE NoDupeTable LIKE DupeTable; 
Alter table NoDupeTable Add Unique `Unique` (CommonField1,CommonField2);
INSERT IGNORE NoDupeTable SELECT * FROM DupeTable;

Другими словами, я создаю дубликат первой таблицы, добавляю уникальный индекс к полям, дубликаты которых я не хочу, и затем делаю Insert IGNORE, который имеет преимущество, заключающееся в том, что он не дает ошибок как обычный Insert если бы он впервые попытался добавить дублирующую запись, основанную на двух полях, и скорее проигнорировал бы любые такие записи.

При перемещении вперед невозможно создать дубликаты записей на основе этих двух полей.

12 голосов
/ 13 апреля 2017

После того, как я сам столкнулся с этой проблемой в огромной базе данных, я не был полностью впечатлен производительностью других ответов. Я хочу сохранить только последнюю дублирующую строку и удалить оставшуюся.

В операторе с одним запросом, без временной таблицы, это работало лучше всего для меня,

DELETE e.*
FROM employee e
WHERE id IN
 (SELECT id
   FROM (SELECT MIN(id) as id
          FROM employee e2
          GROUP BY first_name, last_name
          HAVING COUNT(*) > 1) x);

Единственное предостережение в том, что я должен выполнить запрос несколько раз, но даже при этом я обнаружил, что он работает лучше для меня, чем другие варианты.

7 голосов
/ 03 марта 2016

Вот простой ответ:

delete a from target_table a left JOIN (select max(id_field) as id, field_being_repeated  
    from target_table GROUP BY field_being_repeated) b 
    on a.field_being_repeated = b.field_being_repeated
      and a.id_field = b.id_field
    where b.id_field is null;
6 голосов
/ 02 января 2018

Следующие работы для всех таблиц

CREATE TABLE `noDup` LIKE `Dup` ;
INSERT `noDup` SELECT DISTINCT * FROM `Dup` ;
DROP TABLE `Dup` ;
ALTER TABLE `noDup` RENAME `Dup` ;
5 голосов
/ 31 октября 2016

Эта работа для меня, чтобы удалить старые записи:

delete from table where id in 
(select min(e.id)
    from (select * from table) e 
    group by column1, column2
    having count(*) > 1
); 

Вы можете заменить min (e.id) на max (e.id), чтобы удалить новейшие записи.

4 голосов
/ 09 января 2017
delete p from 
product p
inner join (
    select max(id) as id, url from product 
    group by url 
    having count(*) > 1
) unik on unik.url = p.url and unik.id != p.id;
...