Quasi ogni anagrafica clienti che abbia qualche anno contiene duplicati. Lo stesso cliente registrato dal negozio fisico e dal sito, con il nome scritto in due modi diversi; un'azienda inserita una volta con la ragione sociale completa e una con la sigla; una persona che ha cambiato email e si è registrata di nuovo. I duplicati gonfiano il numero di clienti, dividono la storia degli acquisti, fanno arrivare due volte la stessa comunicazione. Eliminarli richiede metodo, perché unire per errore due persone diverse è peggio che lasciarle separate.
Perché i duplicati non sono uguali
Raramente due record duplicati sono identici. Più spesso differiscono per dettagli:
| Campo | Record 1 | Record 2 |
|---|---|---|
| Nome | Giuseppe Esposito | ESPOSITO GIUSEPPE |
| g.esposito@esempio.it | G.Esposito@Esempio.it | |
| Telefono | 333 123 4567 | +393331234567 |
| Indirizzo | Via Roma 12 | V. Roma, 12 |
Un confronto esatto non trova nulla. Il lavoro si divide quindi in tre fasi: rendere confrontabili i valori, individuare i candidati, decidere e unire.
Fase 1: normalizzare
Normalizzare significa trasformare ogni valore in una forma standard, senza modificare i dati originali: si creano colonne di appoggio o una tabella separata.
- Testo: minuscole, spazi multipli ridotti a uno, spazi iniziali e finali tolti, accenti eventualmente rimossi per il confronto.
- Email: minuscole e senza spazi.
- Telefoni: solo cifre, con il prefisso internazionale aggiunto in modo uniforme.
- Indirizzi: abbreviazioni ricondotte a una forma unica («V.» e «Via» diventano «via»), punteggiatura rimossa.
- Nomi: parole ordinate alfabeticamente, così «Esposito Giuseppe» e «Giuseppe Esposito» coincidono.
SELECT id,
LOWER(TRIM(email)) AS email_norm,
REGEXP_REPLACE(telefono, '[^0-9]', '', 'g') AS tel_norm
FROM clienti;
La sintassi delle espressioni regolari varia tra i database; in alternativa la normalizzazione si fa con uno script.
Fase 2: trovare i candidati
Corrispondenze esatte sui valori normalizzati
Il primo passaggio, il più affidabile, cerca record con lo stesso valore normalizzato su un campo fortemente identificativo: email, codice fiscale, partita IVA, telefono cellulare.
SELECT email_norm, ARRAY_AGG(id) AS ids
FROM clienti_norm
GROUP BY email_norm
HAVING COUNT(*) > 1;
Corrispondenze approssimate
Per nomi e indirizzi servono misure di somiglianza. Le più usate sono:
- distanza di Levenshtein: il numero minimo di caratteri da inserire, cancellare o sostituire per passare da una stringa all'altra. «Rossi» e «Rosi» hanno distanza 1;
- somiglianza per trigrammi: si scompongono le stringhe in gruppi di tre caratteri e si misura quanti ne hanno in comune. PostgreSQL la offre con l'estensione
pg_trgm; - codifiche fonetiche, che assegnano lo stesso codice a parole che suonano simili. Gli algoritmi più noti sono pensati per l'inglese e funzionano male con i cognomi italiani.
Confrontare ogni record con tutti gli altri diventa rapidamente impraticabile: con 100.000 clienti le coppie sono quasi cinque miliardi. Si usa quindi una chiave di blocco: si confrontano solo i record che condividono qualcosa di semplice, come lo stesso CAP o le stesse prime tre lettere del cognome normalizzato. Il numero di confronti si riduce drasticamente.
Combinare più indizi
Un singolo campo simile non basta. Due persone diverse possono chiamarsi Giuseppe Esposito e vivere nella stessa città. Conviene assegnare un punteggio:
- email uguale: indizio forte;
- telefono uguale: indizio forte;
- nome molto simile e stesso indirizzo: indizio forte;
- nome simile e stessa città: indizio debole.
Le coppie con punteggio alto si uniscono, quelle intermedie vanno riviste da una persona, quelle basse si scartano.
Fase 3: unire
La fusione è la parte più delicata, e va pensata prima di toccare i dati.
- Scegliere il record principale, di solito il più vecchio o il più completo.
- Decidere campo per campo quale valore tenere: il più recente, il più completo, quello verificato.
- Spostare i riferimenti: ordini, fatture, ticket e consensi del record secondario vanno aggiornati perché puntino al principale.
- Non cancellare subito il secondario: si marca come unito, conservando un riferimento al principale. Se la fusione era sbagliata, si può tornare indietro.
- Registrare l'operazione in una tabella di storico: quali record, quando, con quale criterio.
BEGIN;
UPDATE ordini SET id_cliente = 101 WHERE id_cliente = 245;
UPDATE ticket SET id_cliente = 101 WHERE id_cliente = 245;
UPDATE clienti SET unito_in = 101, attivo = false WHERE id = 245;
INSERT INTO storico_fusioni (principale, secondario, eseguita_il, criterio)
VALUES (101, 245, now(), 'email uguale');
COMMIT;
Tutto dentro una transazione, così un errore a metà non lascia ordini sparsi tra due record.
Attenzione ai consensi privacy: se un record ha revocato il consenso alle comunicazioni e l'altro no, dopo la fusione deve prevalere la scelta più restrittiva o più recente, non quella che fa comodo.
Prevenire i nuovi duplicati
Ripulire una volta non basta se le cause restano. Le contromisure più efficaci:
- vincoli
UNIQUEsui valori normalizzati, come l'email in minuscolo; - ricerca dei clienti simili prima di crearne uno nuovo, nelle interfacce usate dal personale;
- un unico punto di creazione dei clienti, invece di più sistemi che li creano ciascuno per conto proprio.
In sintesi
- I duplicati reali differiscono per dettagli: prima si normalizzano i valori.
- Le corrispondenze esatte su campi forti sono il primo passaggio, il più sicuro.
- Per nomi e indirizzi servono misure di somiglianza e chiavi di blocco.
- La fusione sposta i riferimenti, conserva il record secondario e registra l'operazione.
- Vincoli e ricerche preventive impediscono che i duplicati si riformino.