A exclusão de duplicatas nas tabelas MySQL é um problema comum, geralmente o resultado de uma restrição ausente para evitar essas duplicatas com antecedência. Mas esse problema comum geralmente vem com necessidades específicas ... que exigem abordagens específicas. A abordagem deve ser diferente, dependendo, por exemplo, do tamanho dos dados, da entrada duplicada que deve ser mantida (geralmente a primeira ou a última), se existem índices a serem mantidos ou se queremos executar qualquer ação adicional. ação nos dados duplicados.
Também existem algumas especificidades no próprio MySQL, como não poder referenciar a mesma tabela em uma causa FROM ao executar uma tabela UPDATE (isso gerará o erro do MySQL # 1093). Essa limitação pode ser superada usando uma consulta interna com uma tabela temporária (conforme sugerido em algumas abordagens acima). Mas essa consulta interna não terá um desempenho especialmente bom ao lidar com fontes de big data.
No entanto, existe uma abordagem melhor para remover duplicatas, eficiente e confiável, e que pode ser facilmente adaptada a diferentes necessidades.
A idéia geral é criar uma nova tabela temporária, geralmente adicionando uma restrição exclusiva para evitar duplicatas adicionais e INSERIR os dados da tabela anterior na nova, enquanto cuida das duplicatas. Essa abordagem baseia-se em consultas simples do MySQL INSERT, cria uma nova restrição para evitar duplicatas adicionais e ignora a necessidade de usar uma consulta interna para procurar duplicatas e uma tabela temporária que deve ser mantida na memória (ajustando também fontes de big data).
É assim que isso pode ser alcançado. Dado que temos um funcionário da tabela , com as seguintes colunas:
employee (id, first_name, last_name, start_date, ssn)
Para excluir as linhas com uma coluna ssn duplicada e mantendo apenas a primeira entrada encontrada, o seguinte processo pode ser seguido:
-- create a new tmp_eployee table
CREATE TABLE tmp_employee LIKE employee;
-- add a unique constraint
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
-- scan over the employee table to insert employee entries
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id;
-- rename tables
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
Explicação técnica
- A linha 1 cria uma nova tabela tmp_eployee com exatamente a mesma estrutura que a tabela employee
- A linha 2 adiciona uma restrição UNIQUE à nova tabela tmp_eployee para evitar outras duplicatas
- A linha 3 varre a tabela original do funcionário por ID, inserindo novas entradas de funcionários na nova tabela tmp_eployee , ignorando as entradas duplicadas
- A linha 4 renomeia as tabelas, para que a nova tabela de funcionários mantenha todas as entradas sem as duplicatas e uma cópia de backup dos dados anteriores seja mantida na tabela backup_employee
⇒ Usando essa abordagem, 1,6 milhões de registros foram convertidos em 6k em menos de 200s.
Chetan , seguindo esse processo, você pode remover rápida e facilmente todas as suas duplicatas e criar uma restrição ÚNICA executando:
CREATE TABLE tmp_jobs LIKE jobs;
ALTER TABLE tmp_jobs ADD UNIQUE(site_id, title, company);
INSERT IGNORE INTO tmp_jobs SELECT * FROM jobs ORDER BY id;
RENAME TABLE jobs TO backup_jobs, tmp_jobs TO jobs;
Obviamente, esse processo pode ser modificado ainda mais para adaptá-lo a diferentes necessidades ao excluir duplicatas. Alguns exemplos a seguir.
✔ Variação para manter a última entrada em vez da primeira
Às vezes, precisamos manter a última entrada duplicada em vez da primeira.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id DESC;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
- Na linha 3, a cláusula ORDER BY id DESC faz com que os últimos IDs tenham prioridade sobre o restante
✔ Variação para executar algumas tarefas nas duplicatas, por exemplo, mantendo uma contagem nas duplicatas encontradas
Às vezes, precisamos executar algum processamento adicional nas entradas duplicadas encontradas (como manter uma contagem das duplicatas).
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
- Na linha 3, uma nova coluna n_duplicates é criada
- Na linha 4, a consulta INSERT INTO ... ON DUPLICATE KEY UPDATE é usada para executar uma atualização adicional quando uma duplicata é encontrada (neste caso, aumentando um contador) A consulta INSERT INTO ... ON DUPLICATE KEY UPDATE pode ser usado para executar diferentes tipos de atualizações para as duplicatas encontradas.
✔ Variação para regenerar o ID do campo auto-incremental
Às vezes, usamos um campo de incremento automático e, para manter o índice o mais compacto possível, podemos aproveitar a exclusão das duplicatas para gerar novamente o campo de incremento automático na nova tabela temporária.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT (first_name, last_name, start_date, ssn) FROM employee ORDER BY id;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
- Na linha # 3, em vez de selecionar todos os campos da tabela, o campo de identificação é ignorado para que o mecanismo do banco de dados gere um novo automaticamente
✔ Outras variações
Muitas modificações adicionais também são possíveis, dependendo do comportamento desejado. Como exemplo, as consultas a seguir usarão uma segunda tabela temporária para, além de 1) manter a última entrada em vez da primeira; e 2) aumentar um contador nas duplicatas encontradas; também 3) regenere o ID do campo auto-incremental, mantendo a ordem de entrada como estava nos dados anteriores.
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0;
INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id DESC ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1;
CREATE TABLE tmp_employee2 LIKE tmp_employee;
INSERT INTO tmp_employee2 SELECT (first_name, last_name, start_date, ssn) FROM tmp_employee ORDER BY id;
DROP TABLE tmp_employee;
RENAME TABLE employee TO backup_employee, tmp_employee2 TO employee;