L'eliminazione dei duplicati sulle tabelle MySQL è un problema comune, che è in genere il risultato di un vincolo mancante per evitare prima quei duplicati. Ma questo problema comune di solito comporta esigenze specifiche ... che richiedono approcci specifici. L'approccio dovrebbe essere diverso a seconda, ad esempio, della dimensione dei dati, della voce duplicata che dovrebbe essere mantenuta (generalmente la prima o l'ultima), se ci sono degli indici da conservare o se vogliamo eseguire ulteriori azione sui dati duplicati.
Ci sono anche alcune specificità su MySQL stesso, come non essere in grado di fare riferimento alla stessa tabella su una causa FROM quando si esegue un aggiornamento della tabella (genererà l'errore MySQL # 1093). Questa limitazione può essere superata utilizzando una query interna con una tabella temporanea (come suggerito in alcuni approcci sopra). Ma questa query interna non funzionerà particolarmente bene quando si ha a che fare con origini di big data.
Tuttavia, esiste un approccio migliore per rimuovere i duplicati, che è sia efficiente che affidabile e che può essere facilmente adattato alle diverse esigenze.
L'idea generale è quella di creare una nuova tabella temporanea, in genere aggiungendo un vincolo univoco per evitare ulteriori duplicati, e INSERIRE i dati dalla tabella precedente a quella nuova, occupandosi dei duplicati. Questo approccio si basa su semplici query INSERT di MySQL, crea un nuovo vincolo per evitare ulteriori duplicati e ignora la necessità di utilizzare una query interna per cercare duplicati e una tabella temporanea che dovrebbe essere mantenuta in memoria (adattando quindi anche le origini di big data).
Ecco come può essere raggiunto. Dato che abbiamo un impiegato di tabella , con le seguenti colonne:
employee (id, first_name, last_name, start_date, ssn)
Per eliminare le righe con una colonna ssn duplicata e mantenere solo la prima voce trovata, è possibile seguire la seguente procedura:
-- 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;
Spiegazione tecnica
- La riga n. 1 crea una nuova tabella tmp_eployee con la stessa struttura della tabella degli impiegati
- La riga 2 aggiunge un vincolo UNICO alla nuova tabella tmp_eployee per evitare ulteriori duplicati
- La riga n. 3 esegue la scansione della tabella dei dipendenti originale per ID, inserendo nuove voci dei dipendenti nella nuova tabella tmp_eployee , ignorando le voci duplicate
- La riga n. 4 rinomina le tabelle, in modo che la nuova tabella dei dipendenti contenga tutte le voci senza i duplicati e una copia di backup dei dati precedenti venga conservata nella tabella backup_employee
⇒ Usando questo approccio, i registri 1.6M sono stati convertiti in 6k in meno di 200s.
Chetan , seguendo questo processo, puoi velocemente e facilmente rimuovere tutti i tuoi duplicati e creare un vincolo UNICO eseguendo:
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;
Naturalmente, questo processo può essere ulteriormente modificato per adattarlo alle diverse esigenze durante l'eliminazione dei duplicati. Seguono alcuni esempi.
✔ Variazione per mantenere l'ultima voce anziché la prima
A volte è necessario mantenere l'ultima voce duplicata anziché la prima.
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;
- Nella riga n. 3, la clausola DESC di ORDER BY id fa in modo che gli ultimi ID abbiano la priorità sugli altri
✔ Variazione per l'esecuzione di alcune attività sui duplicati, ad esempio mantenendo un conteggio sui duplicati trovati
A volte è necessario eseguire ulteriori elaborazioni sulle voci duplicate che si trovano (come tenere un conteggio dei duplicati).
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;
- Alla riga 3, una nuova colonna n_duplica viene creata
- Alla riga 4, la query INSERT INTO ... ON DUPLICATE KEY UPDATE viene utilizzata per eseguire un aggiornamento aggiuntivo quando viene trovato un duplicato (in questo caso, aumentando un contatore) INSERT INTO ... ON DUPLICATE KEY UPDATE query può essere utilizzato per eseguire diversi tipi di aggiornamenti per i duplicati trovati.
✔ Variazione per la rigenerazione dell'ID campo auto-incrementale
A volte utilizziamo un campo auto-incrementale e, per mantenere l'indice il più compatto possibile, possiamo sfruttare l'eliminazione dei duplicati per rigenerare il campo auto-incrementale nella nuova tabella temporanea.
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;
- Alla riga 3, invece di selezionare tutti i campi nella tabella, il campo ID viene ignorato in modo che il motore DB ne generi automaticamente uno nuovo
✔ Ulteriori variazioni
Molte altre modifiche sono anche possibili a seconda del comportamento desiderato. Ad esempio, le seguenti query useranno una seconda tabella temporanea per, oltre a 1) mantenere l'ultima voce invece della prima; e 2) aumentare un segnalino sui duplicati trovati; anche 3) rigenerare l'id del campo auto-incrementale mantenendo l'ordine di immissione come sui dati precedenti.
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;