it-swarm.com.ru

Как удалить дубликаты в таблице MySQL?

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

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

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

Как-то так, но я не знаю, как это сделать.

139
Ali Poder

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

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

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

199
user187291

Предположим, у вас есть таблица 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  
112
Abhijoy_D

После удаления дубликатов для всех 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
)
53
Kamil Szot

Удаление повторяющихся строк в 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), которое соответствует левому только тому, который IS макс.

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

Сделайте резервную копию таблицы, прежде чем запустить это. 

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

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

45
Eric Leschinski

Кажется, это всегда работает для меня:

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, как при первой попытке добавить дубликат записи на основе двух полей и, скорее, игнорировать любые такие записи.

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

12
user3649739

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

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

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);

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

9
seaders

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

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;
7
Ted Celestin

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

CREATE TABLE `noDup` LIKE `Dup` ;
INSERT `noDup` SELECT DISTINCT * FROM `Dup` ;
DROP TABLE `Dup` ;
ALTER TABLE `noDup` RENAME `Dup` ;
6
M.B.Miri

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

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), чтобы удалить новейшие записи.

5
richardhell

Эта процедура удалит все дубликаты (включая кратные) в таблице, сохраняя последний дубликат. Это расширение Получение последней записи в каждой группе

Надеюсь, это кому-нибудь пригодится.

DROP TABLE IF EXISTS UniqueIDs;
CREATE Temporary table UniqueIDs (id Int(11));

INSERT INTO UniqueIDs
    (SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON
    (T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison Fields 
    AND T1.ID < T2.ID)
    WHERE T2.ID IS NULL);

DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);
4
Simon
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;
3
temonehm

Еще один простой способ ... с помощью UPDATE IGNORE:

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

Добавьте временный справочный столбец, чтобы отметить уникальность:

ALTER TABLE `yourtable` ADD `unique` VARCHAR(3) NOT NULL AFTER `lastcolname`;

=> это добавит столбец к вашей таблице. 

Обновите таблицу, попытайтесь пометить все как уникальные, но игнорируйте возможные ошибки из-за дублирования ключа (записи будут пропущены):

UPDATE IGNORE `yourtable` SET `unique` = 'Yes' WHERE 1;

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

Удалите все, что не уникально:

DELETE * FROM `yourtable` WHERE `unique` <> 'Yes';

=> Это удалит все дубликаты записей.

Оставьте столбец ...

ALTER TABLE `yourtable` DROP `unique`;
2
Werner

Я считаю решение Вернера выше наиболее удобным, поскольку оно работает независимо от наличия первичного ключа, не связывается с таблицами, использует простой SQL-файл, ориентированный на будущее, очень понятно.

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

1) добавить новый логический столбец

alter table mytable add tokeep boolean;

2) добавить ограничение на дублированные столбцы И новый столбец

alter table mytable add constraint preventdupe unique (mycol1, mycol2, tokeep);

3) установите для логического столбца значение true. Это будет успешным только в одной из дублированных строк из-за нового ограничения

update ignore mytable set tokeep = true;

4) удалить строки, которые не были помечены как tokeep

delete from mytable where tokeep is null;

5) опустить добавленный столбец

alter table mytable drop tokeep;

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

1
xtian

Удаление дубликатов в таблицах MySQL является распространенной проблемой, которая обычно сопровождается особыми потребностями. В случае, если кому-то интересно, здесь ( Удалить дубликаты строк в MySQL ) я объясняю, как использовать временную таблицу для надежного и быстрого удаления дубликатов MySQL, а также для обработки больших источников данных (с примерами для различного использования случаев).

ALi , в вашем случае вы можете запустить что-то вроде этого:

-- create a new temporary table
CREATE TABLE tmp_table1 LIKE table1;

-- add a unique constraint    
ALTER TABLE tmp_table1 ADD UNIQUE(sid, title);

-- scan over the table to insert entries
INSERT IGNORE INTO tmp_table1 SELECT * FROM table1 ORDER BY sid;

-- rename tables
RENAME TABLE table1 TO backup_table1, tmp_table1 TO table1;
1
César Revert-Gomar

Это работает для больших таблиц:

 CREATE Temporary table duplicates AS select max(id) as id, url from links group by url having count(*) > 1;

 DELETE l from links l inner join duplicates ld on ld.id = l.id WHERE ld.id IS NOT NULL;

Чтобы удалить самое старое изменение max(id) на min(id)

0
Mugoma J. Okomba

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

Создает точную копию вашего стола

создать таблицу temp_table как oldtablename; вставить temp_table select * from oldtablename;

Опорожняет ваш оригинальный стол

УДАЛИТЬ * от oldtablename;

Копирует все отдельные значения из скопированной таблицы обратно в исходную таблицу

ВСТАВИТЬ oldtablename SELECT * из группы temp_table по имени, фамилии, dob

Удаляет вашу временную таблицу.

Удалить таблицу temp_table

Вам нужно сгруппировать по всем полям, которые вы хотите сохранить отдельно.

0
ChrisAardvark
DELETE T2
FROM   table_name T1
JOIN   same_table_name T2 ON (T1.title = T2.title AND T1.ID <> T2.ID)
0
Nav
delete from `table` where `table`.`SID` in 
    (
    select t.SID from table t join table t1 on t.title = t1.title  where t.SID > t1.SID
)
0
Patrick

Ответ Love @ eric, но он не работает, если у вас действительно большая таблица (я получаю The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay, когда пытаюсь ее запустить). Таким образом, я ограничил запрос соединения только рассмотрением дублирующихся строк, и в итоге я получил:

DELETE a FROM penguins a
    LEFT JOIN (SELECT COUNT(baz) AS num, MIN(baz) AS keepBaz, foo
        FROM penguins
        GROUP BY deviceId HAVING num > 1) b
        ON a.baz != b.keepBaz
        AND a.foo = b.foo
    WHERE b.foo IS NOT NULL

Предложение WHERE в этом случае позволяет MySQL игнорировать любую строку, у которой нет дубликата, а также игнорирует, если это первый экземпляр дубликата, поэтому будут игнорироваться только последующие дубликаты. Измените MIN(baz) на MAX(baz), чтобы сохранить последний экземпляр вместо первого.

0
Gujamin

Это здесь сделает столбец column_name первичным ключом, а тем временем проигнорирует все ошибки. Таким образом, будут удалены строки с дублирующимся значением для column_name.

ALTER IGNORE TABLE `table_name` ADD PRIMARY KEY (`column_name`);
0
Abraham Murciano Benzadon