Как удалить дубликаты записей в базе данных mysql?

каков наилучший способ удаления дубликатов записей в базе данных mysql с помощью rails или mysql-запросов?

15 ответов


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

 select distinct * into NewTable from MyTable

вот еще одна идея в любом языке:

rs = `select a, b, count(*) as c from entries group by 1, 2 having c > 1`
rs.each do |a, b, c|
  `delete from entries where a=#{a} and b=#{b} limit #{c - 1}`
end

Edit:

спасибо Олаф для этого "иметь" подсказку :)


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

class ActiveRecord::Base
  def non_id_attributes
    atts = self.attributes
    atts.delete('id')
    atts
  end
end

duplicate_groups = YourClass.find(:all).group_by { |element| element.non_id_attributes }.select{ |gr| gr.last.size > 1 }
redundant_elements = duplicate_groups.map { |group| group.last - [group.last.first] }.flatten
redundant_elements.each(&:destroy)

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

SELECT DISTINCT(req_field) AS field, COUNT(req_field) AS fieldCount FROM 
table_name GROUP BY req_field HAVING fieldCount > 1


Удалить Повторяющиеся Запросы:

DELETE FROM table_name 
USING table_name, table_name AS vtable 
WHERE 
    (table_name.id > vtable.id) 
AND (table_name.req_field=req_field)

заменить req_field и имя_таблицы - должен работать без каких-либо проблем.


новое в SQL :-) Это классический вопрос-часто задаваемый в интервью:-) Я не знаю, будет ли он работать в MYSQL, но он работает в большинстве баз данных -

> create table t(
>     a char(2),
>     b char(2),
>     c smallint )

> select a,b,c,count(*) from t
> group by a,b,c
> having count(*) > 1
a  b  c
-- -- ------ -----------
(0 rows affected)

> insert into t values ("aa","bb",1)
(1 row affected)

> insert into t values ("aa","bb",1)
(1 row affected)

> insert into t values ("aa","bc",1)
(1 row affected)

> select a,b,c,count(*) from t group by a,b,c having count(*) > 1
a  b  c 
-- -- ------ -----------
aa bb      1           2
(1 row affected)

Если у вас есть PK (id) в таблице (EMP) и вы хотите удалить дубликаты записей с именем столбца. Для больших данных следующий запрос может быть хорошим подходом.

DELETE t3
FROM (
        SELECT t1.name, t1.id
        FROM (
                SELECT name
                FROM EMP
                GROUP BY name
                HAVING COUNT(name) > 1
        ) AS t0 INNER JOIN EMP t1 ON t0.name = t1.name
) AS t2 INNER JOIN EMP t3 ON t3.name = t2.name
WHERE t2.id < t3.id;

предположим, у нас есть имя таблицы tbl_product и есть дублирование в области p_pi_code и p_nats_id в максимуме нет отсчета тогда сначала создайте новую таблицу вставьте данные из существующей таблицы ...
ie от tbl_product to newtable1 если что-то еще, то newtable1 to newtable2

CREATE TABLE `newtable2` (                                  
            `p_id` int(10) unsigned NOT NULL auto_increment,         
            `p_status` varchar(45) NOT NULL,                         
            `p_pi_code` varchar(45) NOT NULL,                        
            `p_nats_id` mediumint(8) unsigned NOT NULL,              
            `p_is_special` tinyint(4) NOT NULL,                      
             PRIMARY KEY (`p_id`)                                   
      ) ENGINE=InnoDB;

INSERT INTO newtable1 (p_status, p_pi_code, p_nats_id, p_is_special) SELECT 
    p_status, p_pi_code, p_nats_id, p_is_special FROM tbl_product group by p_pi_code;

INSERT INTO newtable2 (p_status, p_pi_code, p_nats_id, p_is_special) SELECT 
    p_status, p_pi_code, p_nats_id, p_is_special FROM newtable1 group by p_nats_id;

после этого мы видим, что все дублирование в поле удалено


Я должен сделайте это недавно на Oracle, но шаги были бы такими же на MySQL. Это было много данных, по крайней мере, по сравнению с тем, с чем я привык работать, поэтому мой процесс де-dup был сравнительно тяжелым. Я включаю его здесь, Если кто-то еще столкнется с подобной проблемой.

мои дубликаты записей имели разные идентификаторы, разные updated_at раз, возможно, разное updated_by IDs, но все остальные столбцы одинаковы. Я хотел сохранить больше всего. недавно обновлен любой дубликат набора.

я использовал комбинацию логики Rails и SQL, чтобы сделать это.

Шаг первый: запустите сценарий rake для идентификации идентификаторов повторяющихся записей, используя логику модели. Идентификаторы идут в текстовый файл.

Шаг второй: создайте временную таблицу с одним столбцом, идентификаторы для удаления, загруженные из текстового файла.

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

CREATE TABLE temp_duplicate_models 
  AS (SELECT * FROM models 
  WHERE id IN (SELECT * FROM temp_duplicate_ids));

Шаг четвертый: фактическое удаление.

DELETE FROM models WHERE id IN (SELECT * FROM temp_duplicate_ids);

вы можете использовать:

http://lenniedevilliers.blogspot.com/2008/10/weekly-code-find-duplicates-in-sql.html

чтобы получить дубликаты, а затем просто удалить их с помощью кода Ruby или кода SQL (я бы сделал это в коде SQL, но это зависит от вас: -)


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

DELETE FROM table WHERE pk_id IN(
   SELECT DISTINCT t3.pk_id FROM (
       SELECT t1.* FROM table AS t1 INNER JOIN (
           SELECT col1, col2, col3, col4, COUNT(*) FROM table
           GROUP BY col1, col2, col3, col4 HAVING COUNT(*)>1) AS t2
       ON t1.col1 = t2.col1 AND t1.col2 = t2.col2 AND t1.col3 = t2.col3 AND
       t1.col4 = t2.col4)
   AS t3, (
       SELECT t1.* FROM table AS t1 INNER JOIN (
           SELECT col1, col2, col3, col4, COUNT(*) FROM table
           GROUP BY col1, col2, col3, col4 HAVING COUNT(*)>1) AS t2
       ON t1.col1 = t2.col1 AND t1.col2 = t2.col2 AND t1.col3 = t2.col3 AND
       t1.col4 = t2.col4)
   AS t4
   WHERE t3.col1 = t4.col1 AND t3.pk_id > t4.pk_id

)

это оставит первую запись, введенную в базу данных, удалив "новейшие" дубликаты. Если вы хотите сохранить последнюю запись, переключите > в <.>


в MySql, когда я ставлю что-то вроде

delete from A where IDA in (select IDA from A )

mySql сказал что-то вроде "Вы не можете использовать ту же таблицу в select части операции удаления."

мне просто нужно удалить некоторые дубликаты записей ,и мне это удалось.php программа, как это

<?php
...
$res = hacer_sql("SELECT MIN(IDESTUDIANTE) as IDTODELETE 
FROM `estudiante` group by `LASTNAME`,`FIRSTNAME`,`CI`,`PHONE`
HAVING COUNT(*) > 1 )");
while ( $reg = mysql_fetch_assoc($res) ) {
   hacer_sql("delete from estudiante where IDESTUDIANTE = {$reg['IDTODELETE']}");
}
?>

Я использую Alter Table

ALTER IGNORE TABLE jos_city ADD UNIQUE INDEX(`city`);

я использовал ответ @krukid выше, чтобы сделать следующее На таблице с примерно 70 000 записей:

rs = 'select a, b, count(*) as c from table group by 1, 2 having c > 1'

# get a hashmap
dups = MyModel.connection.select_all(rs)

# convert to array
dupsarr = dups.map { |i|  [i.a, i.b, i.c] }

# delete dups
dupsarr.each do |a,b,c|
    ActiveRecord::Base.connection.execute("delete from table_name where a=#{MyModel.sanitize(a)} and b=#{MyModel.sanitize(b)} limit #{c-1}")
end

вот решение rails, которое я придумал. Может быть, не самый эффективный, но не большое дело, если его однократная миграция.

distinct_records = MyTable.all.group(:distinct_column_1, :distinct_column_2).map {|mt| mt.id}
duplicates = MyTable.all.to_a.reject!{|mt| distinct_records.include? mt.id}
duplicates.each(&:destroy)

во-первых, группы по всем столбцам, которые определяют уникальность, пример показывает 2, но вы могли бы иметь более или менее

во-вторых, выбирает обратную от этой группы...все остальные записи

в-третьих, удаляет все эти записи.


сначала сделайте группу по столбцу, на котором вы хотите удалить дубликат.Но я не делаю этого с группой by.Я пишу self join.

вам не нужно создавать временную таблицу.

удалить дубликат, кроме одной записи: В этой таблице он должен иметь столбец auto increment. Возможное решение, с которым я только что столкнулся:

DELETE n1 FROM names n1, names n2 WHERE n1.id > n2.id AND n1.name = n2.name

Если вы хотите сохранить строку с наименьшим значением Auto increment id или

DELETE n1 FROM names n1, names n2 WHERE n1.id < n2.id AND n1.name = n2.name

Если вы хотите сохранить строка с наибольшим значением Auto increment id.

вы можете перекрестно проверить свое решение, снова найти дубликат:

SELECT * FROM `names` GROUP BY name, id having count(name) > 1;

если он возвращает 0 результат, то запрос выполняется успешно.