Как удалить повторяющиеся записи из БД MySQL?

У меня есть таблица с некоторыми идентификаторами + титулы. Я хочу сделать столбец заголовка уникальным, но у него уже есть более 600k записей, некоторые из которых являются дубликатами (иногда несколько десятков раз).

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

8 ответов


эта команда добавляет уникальный ключ, и удаляет все строки, которые генерируют ошибки (из-за уникального ключа). Это удаляет дубликаты.

ALTER IGNORE TABLE table ADD UNIQUE KEY idx1(title); 

Edit: обратите внимание, что эта команда может не работать для таблиц InnoDB для некоторых версий MySQL. См.этот пост для решения проблемы. (Спасибо "анонимному пользователю" за эту информацию.)


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

CREATE TABLE tmp_table AS SELECT DISTINCT [....] FROM main_table

более конкретно:
Более быстрый способ-вставить отдельные строки во временную таблицу. Используя delete, мне потребовалось несколько часов, чтобы удалить дубликаты из таблицы из 8 миллионов строк. Использование insert и distinct заняло всего 13 минут.

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;  
DROP TABLE tableName;  
INSERT tableName SELECT * FROM tempTableName;  
DROP TABLE tempTableName;  

Это показывает, как это сделать в SQL2000. Я не совсем знаком с синтаксисом MySQL, но я уверен, что есть что-то сопоставимое

create table #titles (iid int identity (1, 1), title varchar(200))

-- Repeat this step many times to create duplicates
insert into #titles(title) values ('bob')
insert into #titles(title) values ('bob1')
insert into #titles(title) values ('bob2')
insert into #titles(title) values ('bob3')
insert into #titles(title) values ('bob4')


DELETE T  FROM 
#titles T left join 
(
  select title, min(iid) as minid from #titles group by title
) D on T.title = D.title and T.iid = D.minid
WHERE D.minid is null

Select * FROM #titles

delete from student where id in (
SELECT distinct(s1.`student_id`) from student as s1 inner join student as s2
where s1.`sex` = s2.`sex` and
s1.`student_id` > s2.`student_id` and
s1.`sex` = 'M'
    ORDER BY `s1`.`student_id` ASC
)

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

однако у него есть одна проблема:

ошибка 1093 (HY000): вы не можете указать целевую таблицу "студент" для обновить предложение FROM

это, однако, может быть разрешено с помощью (SELECT * FROM student) вместо student:

DELETE FROM student WHERE id IN (
SELECT distinct(s1.`student_id`) FROM (SELECT * FROM student) AS s1 INNER JOIN (SELECT * FROM student) AS s2
WHERE s1.`sex` = s2.`sex` AND
s1.`student_id` > s2.`student_id` AND
s1.`sex` = 'M'
ORDER BY `s1`.`student_id` ASC
)

дать ваши +1 Нитин придумали оригинальное решение.


С MySql ALTER IGNORE TABLE был осужден, вам нужно фактически удалить дубликат даты перед добавлением индекса.

сначала написать запрос, который находит все дубликаты. Здесь я предполагаю, что email - это поле, содержащее дубликаты.

SELECT
    s1.email
    s1.id, 
    s1.created
    s2.id,
    s2.created 
FROM 
    student AS s1 
INNER JOIN 
    student AS s2 
WHERE 
    /* Emails are the same */
    s1.email = s2.email AND
    /* DON'T select both accounts,
       only select the one created later.
       The serial id could also be used here */
    s2.created > s1.created 
;

Далее выберите только уникальные дубликаты идентификаторов:

SELECT 
    DISTINCT s2.id
FROM 
    student AS s1 
INNER JOIN 
    student AS s2 
WHERE 
    s1.email = s2.email AND
    s2.created > s1.created 
;

как только вы убедитесь, что содержит только дубликаты идентификаторов, которые вы хотите удалить, запустите delete. Вы должны добавить (SELECT * FROM tblname) так что MySql не жалуется.

DELETE FROM
    student 
WHERE
    id
IN (
    SELECT 
        DISTINCT s2.id
    FROM 
        (SELECT * FROM student) AS s1 
    INNER JOIN 
        (SELECT * FROM student) AS s2 
    WHERE 
        s1.email = s2.email AND
        s2.created > s1.created 
);

затем создайте уникальный индекс:

ALTER TABLE
    student
ADD UNIQUE INDEX
    idx_student_unique_email(email)
;

ниже запрос может быть использован для удаления всех дубликатов, кроме одной строки с наименьшим значением поля "id"

DELETE t1 FROM table_name t1, table_name t2 WHERE t1.id > t2.id AND t1.name = t2.name

аналогичным образом мы можем сохранить строку с самым высоким значением в " id " следующим образом

 DELETE t1 FROM table_name t1, table_name t2 WHERE t1.id < t2.id AND t1.name = t2.name

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

в этом случае, что-то вроде этого должно работать:

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

-- add a unique constraint    
ALTER TABLE tmp_table1 ADD UNIQUE(id, 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;