it-swarm.com.ru

MySQL быстро удаляет дубликаты из большой базы данных

У меня большая (> Mil строки) база данных MySQL, испорченная дубликатами. Я думаю, что это может быть от 1/4 до 1/2 всей базы данных, заполненной ими .... Мне нужно быстро избавиться от них (я имею в виду время выполнения запроса) .... Вот как это выглядит:
id (index) | текст1 | текст2 | text3
Комбинация text1 и text2 должна быть уникальной, если есть дубликаты, должна остаться только одна комбинация с text3 NOT NULL. Пример: 

1 | abc | def | NULL  
2 | abc | def | ghi  
3 | abc | def | jkl  
4 | aaa | bbb | NULL  
5 | aaa | bbb | NULL  

... становится: 

1 | abc | def | ghi   #(doesn't realy matter id:2 or id:3 survives)   
2 | aaa | bbb | NULL  #(if there's no NOT NULL text3, NULL will do)

Новые идентификаторы могут быть чем угодно, они не зависят от старых идентификаторов таблиц.
Я пробовал такие вещи, как:

CREATE TABLE tmp SELECT text1, text2, text3
FROM my_tbl;
GROUP BY text1, text2;
DROP TABLE my_tbl;
ALTER TABLE tmp RENAME TO my_tbl;

Или ВЫБЕРИТЕ DISTINCT и другие варианты.
Хотя они работают с небольшими базами данных, время выполнения запросов у меня просто огромное (на самом деле никогда не доходило до конца;> 20 минут)

Есть ли более быстрый способ сделать это? Пожалуйста, помогите мне решить эту проблему.

69
bizzz

Я считаю, что это будет сделано, используя дубликат ключа + ifnull ():

create table tmp like yourtable;

alter table tmp add unique (text1, text2);

insert into tmp select * from yourtable 
    on duplicate key update text3=ifnull(text3, values(text3));

rename table yourtable to deleteme, tmp to yourtable;

drop table deleteme;

Должно быть намного быстрее, чем что-либо, что требует группирования по отдельности или подзапроса, или даже упорядочения по. Это даже не требует файловой сортировки, которая снижает производительность на большой временной таблице. Все равно потребуется полное сканирование оригинальной таблицы, но избежать этого нельзя.

146
ʞɔıu

Нашел этот простой 1-строчный код, чтобы сделать именно то, что мне нужно:

ALTER IGNORE TABLE dupTest ADD UNIQUE INDEX(a,b);

Взято из: http://mediakey.dk/~cc/mysql-remove-duplicate-entries/

95
liorq
DELETE FROM dups
WHERE id NOT IN(
    SELECT id FROM (
        SELECT DISTINCT id, text1, text2
            FROM dups
        GROUP BY text1, text2
        ORDER BY text3 DESC
    ) as tmp
)

Это запрашивает все записи, группы по полям различия и порядкам по идентификатору (означает, что мы выбираем первую не нулевую запись text3). Затем мы выбираем идентификаторы из этого результата (это хорошие идентификаторы ... они не будут удалены) и удаляем все идентификаторы, которые НЕ те.

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

После того, как вы сделали это «исправление», я бы применил к этой таблице уникальный индекс (text1, text2). Для предотвращения возможности дублирования в будущем.

Если вы хотите пойти «создать новую таблицу и заменить старую» маршрут. Вы можете использовать самый внутренний оператор select для создания вашего оператора вставки.

Специфично для MySQL (предполагается, что новая таблица называется my_tbl2 и имеет точно такую ​​же структуру):

INSERT INTO my_tbl2
    SELECT DISTINCT id, text1, text2, text3
            FROM dups
        GROUP BY text1, text2
        ORDER BY text3 DESC

Смотрите MySQL INSERT ... SELECT для получения дополнительной информации.

12
Kevin Peno

удалить дубликаты без удаления внешних ключей

create table tmp like mytable;
ALTER TABLE tmp ADD UNIQUE INDEX(text1, text2, text3, text4, text5, text6);
insert IGNORE into tmp select * from mytable;
delete from mytable where id not in ( select id from tmp);
8
gadelkareem

Если вы можете создать новую таблицу, сделайте это с помощью уникального ключа в полях text1 + text2. Затем вставьте в таблицу игнорирование ошибок (используя синтаксис INSERT IGNORE): 

select * from my_tbl order by text3 desc
  • Я думаю, что порядок по text3 desc установит значения NULL последними, но дважды проверьте это.

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

3
Scott Saunders

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

Пример приведен здесь .

1
user1931858

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

 DELETE i1 
FROM TABLE i1
LEFT JOIN TABLE i2
  ON i1.id = i2.id
 AND i1.colo = i2.customer_invoice_id
 AND i1.id < i2.id
WHERE i2.customer_invoice_id IS NOT NULL
0
kamran Sheikh

Я знаю, что это старый поток, но у меня есть несколько messy метод, который намного быстрее и настраиваемый, с точки зрения скорости, я бы сказал, 10 секунд вместо 100 секунд (10: 1).

Мой метод действительно требовал всего того, что вы пытались избежать в messy:

  • Группировать по (и иметь)
  • групповой конкат с ORDER BY
  • 2 временных стола
  • используя файлы на диске!
  • каким-то образом (php?) удалить файл после

Но когда вы говорите о МИЛЛИОНАХ (или, в моем случае, Десятках миллионов), это того стоит.

во всяком случае, это не так много, потому что комментарии на португальском, но вот мой пример:

EDIT: если я получу комментарии, я объясню дальше, как это работает :)

START TRANSACTION;

DROP temporary table if exists to_delete;

CREATE temporary table to_delete as (
    SELECT
        -- escolhe todos os IDs duplicados menos os que ficam na BD
        -- A ordem de escolha dos IDs é dada por "ORDER BY campo_ordenacao DESC" em que o primeiro é o que fica
        right(
            group_concat(id ORDER BY campos_ordenacao DESC SEPARATOR ','),
            length(group_concat(id ORDER BY campos_ordenacao DESC SEPARATOR ',')) 
                - locate(",",group_concat(id ORDER BY campos_ordenacao DESC SEPARATOR ','))
        ) as ids,

        count(*) as c

    -- Tabela a eliminar duplicados
    FROM teste_dup

    -- campos a usar para identificar  duplicados
    group by test_campo1, test_campo2, teste_campoN
    having count(*) > 1 -- é duplicado
);

-- aumenta o limite desta variável de sistema para o máx 
SET SESSION group_concat_max_len=4294967295;

-- envia os ids todos a eliminar para um ficheiro
select group_concat(ids SEPARATOR ',') from to_delete INTO OUTFILE 'sql.dat';

DROP temporary table if exists del3;
create temporary table del3 as (select CAST(1 as signed) as ix LIMIT 0);

-- insere os ids a eliminar numa tabela temporaria a partir do ficheiro
load data infile 'sql.dat' INTO TABLE del3
LINES TERMINATED BY ',';

alter table del3 add index(ix);

-- elimina os ids seleccionados
DELETE teste_dup -- tabela 
from teste_dup -- tabela

join del3 on id=ix;

COMMIT;
0
JDuarteDJ

У меня нет большого опыта работы с MySQL. Если у него есть аналитические функции, попробуйте:

 удалить из my_tbl 
 где id в (
 выберите id 
 from (выберите id, row_number () 
 over (разбиение по text1, упорядочение text2 по text3 desc) как rn 
 из my_tbl 
/* необязательно: где text1 как 'a%' */
) как t2 
, где rn> 1 
) 

необязательное предложение where означает, что вам придется запускать его несколько раз, по одному для каждой буквы и т. д. Создать индекс для text1?

Перед запуском убедитесь, что «text desc» будет сортировать нули последними в MySQL.

0
redcayuga