Eliminar registros duplicados
Una vez que has identificado los registros duplicados, el siguiente paso es eliminarlos conservando exactamente uno de cada grupo. Esta operación requiere cuidado porque un DELETE mal construido puede borrar todos los registros, incluido el que querías conservar. MySQL ofrece varias técnicas seguras para realizar esta limpieza.
Conservar el registro con menor ID
La estrategia más común es conservar el registro más antiguo (el que tiene el ID más bajo) y eliminar los demás. Usa un DELETE con subconsulta:
DELETE c1
FROM clientes c1
JOIN clientes c2
ON c1.email = c2.email
AND c1.id > c2.id;Esta sentencia compara cada registro con todos los demás que tienen el mismo email. Solo elimina los registros cuyo ID es mayor que otro con el mismo email, lo que garantiza que el de menor ID sobrevive.
Antes de ejecutar el DELETE, verifica qué se va a eliminar cambiando DELETE por SELECT:
-- Previsualizar qué se eliminará
SELECT c1.*
FROM clientes c1
JOIN clientes c2
ON c1.email = c2.email
AND c1.id > c2.id;| id | nombre | |
|---|---|---|
| 89 | Maria Garcia López | maria.garcia@email.com |
| 234 | María García L. | maria.garcia@email.com |
| 156 | Carlos López Martín | carlos.lopez@email.com |
Conservar el registro más reciente
Si prefieres conservar el registro más reciente:
DELETE c1
FROM clientes c1
JOIN clientes c2
ON c1.email = c2.email
AND c1.id < c2.id;Simplemente invertimos la comparación: ahora se eliminan los registros con ID menor, conservando el más alto.
Eliminar con ROW_NUMBER()
Las funciones de ventana ofrecen un enfoque más flexible:
DELETE FROM clientes
WHERE id IN (
SELECT id FROM (
SELECT id,
ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) AS rn
FROM clientes
) ranked
WHERE rn > 1
);ROW_NUMBER() asigna un número secuencial a cada fila dentro de cada grupo de email. Las filas con rn > 1 son duplicados. La subconsulta intermedia es necesaria porque MySQL no permite referenciar directamente la tabla que se está modificando en una subconsulta del DELETE.
Duplicados basados en múltiples columnas
Cuando el duplicado se define por varias columnas, ajusta la condición del JOIN:
DELETE p1
FROM productos p1
JOIN productos p2
ON p1.nombre = p2.nombre
AND p1.categoria_id = p2.categoria_id
AND p1.id > p2.id;O con ROW_NUMBER():
DELETE FROM productos
WHERE id IN (
SELECT id FROM (
SELECT id,
ROW_NUMBER() OVER (
PARTITION BY nombre, categoria_id
ORDER BY id
) AS rn
FROM productos
) ranked
WHERE rn > 1
);Técnica con tabla temporal
Para mayor seguridad, puedes usar una tabla temporal como intermediaria:
-- 1. Crear tabla temporal con los IDs a conservar
CREATE TEMPORARY TABLE ids_conservar AS
SELECT MIN(id) AS id
FROM clientes
GROUP BY email;
-- 2. Eliminar todo lo que no esté en la lista
DELETE FROM clientes
WHERE id NOT IN (SELECT id FROM ids_conservar);
-- 3. Limpiar
DROP TEMPORARY TABLE ids_conservar;Esta técnica es más explícita y fácil de verificar paso a paso. Puedes examinar la tabla temporal antes de ejecutar el DELETE para confirmar que contiene los IDs correctos.
Verificar después de la limpieza
Después de eliminar duplicados, verifica que no queden:
SELECT email, COUNT(*) AS veces
FROM clientes
GROUP BY email
HAVING COUNT(*) > 1;Si la consulta no devuelve filas, la limpieza fue exitosa. Ahora es el momento de añadir una restricción para prevenir futuros duplicados:
ALTER TABLE clientes ADD UNIQUE INDEX uk_email (email);Precauciones importantes
Siempre haz un backup antes de eliminar datos. Un DELETE masivo es una operación destructiva que no se puede deshacer fuera de una transacción.
Si usas InnoDB, envuelve la operación en una transacción para poder revertir si algo sale mal:
START TRANSACTION;
DELETE c1
FROM clientes c1
JOIN clientes c2
ON c1.email = c2.email
AND c1.id > c2.id;
-- Verificar el resultado
SELECT COUNT(*) FROM clientes;
-- Si todo es correcto
COMMIT;
-- Si algo salió mal
-- ROLLBACK;Para tablas muy grandes con muchos duplicados, eliminar todo de golpe puede generar un gran uso de recursos. Considera eliminar en lotes:
-- Eliminar en lotes de 1000
DELETE c1
FROM clientes c1
JOIN clientes c2
ON c1.email = c2.email
AND c1.id > c2.id
LIMIT 1000;
-- Repetir hasta que no queden duplicadosLa eliminación de duplicados es una operación de limpieza que idealmente solo necesitas hacer una vez. Después de limpiar, establece restricciones de unicidad para que la base de datos impida la entrada de duplicados en el futuro.
Escrito por Eduardo Lázaro
