Una importación desde Excel y un formulario de registro pueden crear dos fichas para la misma persona. Si cada ficha tiene pedidos asociados, borrar una directamente puede romper el historial o eliminar información vinculada. En MySQL puedes localizar los registros sospechosos, revisar qué información conservar y aplicar una restricción que impida nuevas duplicaciones.
1. Define qué cuenta como duplicado
Para este ejemplo, una tienda admite una ficha por correo electrónico y considera equivalentes las variantes con espacios exteriores o diferencias de mayúsculas. Dos personas con el mismo nombre pueden ser clientes distintos. Si tu negocio permite correos compartidos, usa otra clave de identidad. En un CRM con varias empresas, normalmente la regla se aplica por empresa.
2. Detecta correos repetidos con GROUP BY y HAVING
SELECT LOWER(TRIM(email)) COLLATE utf8mb4_0900_as_ci AS email_normalizado,
COUNT(*) AS total
FROM clientes
WHERE email IS NOT NULL
AND TRIM(email) <> ''
GROUP BY email_normalizado
HAVING COUNT(*) > 1
ORDER BY total DESC, email_normalizado;
El ejemplo usa MySQL 8 y presupone que email está en utf8mb4. La colación elegida ignora diferencias de mayúsculas y distingue acentos. Usa el mismo criterio de comparación al detectar duplicados y al crear el índice, para que ambas operaciones agrupen los mismos valores.
Si aparece ana@example.com, revisa sus fichas antes de decidir cuál conservar:
SELECT id, nombre, email, telefono
FROM clientes
WHERE LOWER(TRIM(email)) COLLATE utf8mb4_0900_as_ci = 'ana@example.com'
ORDER BY id;
Compara los datos de contacto, direcciones y referencias externas. Una ficha reciente puede tener el teléfono actualizado y la antigua puede concentrar el historial. Conserva el ID más conveniente y transfiere los datos válidos según las reglas del negocio.
3. Unifica las fichas sin perder sus pedidos
Supongamos que confirmaste que los clientes 101 y 205 son la misma persona y decidiste conservar el 101. Antes de ejecutar cambios, haz un respaldo y prueba la migración en una copia. Revisa todas las referencias a clientes.id, incluidas las que la aplicación maneja sin una clave foránea. En el ejemplo solo se muestra la tabla pedidos.
START TRANSACTION;
SELECT id, nombre, email, telefono
FROM clientes
WHERE id IN (101, 205)
ORDER BY id
FOR UPDATE;
SELECT cliente_id, COUNT(*) AS total_pedidos
FROM pedidos
WHERE cliente_id IN (101, 205)
GROUP BY cliente_id;
UPDATE pedidos
SET cliente_id = 101
WHERE cliente_id = 205;
SELECT COUNT(*) AS pedidos_aun_vinculados
FROM pedidos
WHERE cliente_id = 205;
-- Continúa solo si la revisión devuelve cero y migraste las demás referencias.
DELETE FROM clientes
WHERE id = 205;
-- Si verificaste el resultado, ejecuta COMMIT.
-- Ante un error o resultado inesperado, ejecuta ROLLBACK.
Usa InnoDB para que la transacción cubra los cambios. Ejecuta las comprobaciones antes de confirmar. Si una tabla relacionada tiene una restricción propia, por ejemplo una sola dirección principal por cliente, resuelve ese conflicto antes de mover sus filas. Registra el ID eliminado y el que conservaste para poder rastrear la fusión.
4. Aplica una restricción UNIQUE a la clave normalizada
Una vez resueltos todos los grupos duplicados, puedes generar una columna con el correo normalizado y hacerla única. En este ejemplo el correo es opcional y la columna original admite hasta 254 caracteres. Ajusta la longitud al esquema real y revisa el impacto del cambio en una copia representativa.
ALTER TABLE clientes
ADD COLUMN email_normalizado VARCHAR(254)
CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci
GENERATED ALWAYS AS (NULLIF(LOWER(TRIM(email)), '')) STORED,
ADD CONSTRAINT uq_clientes_email_normalizado
UNIQUE (email_normalizado);
MySQL permite varios NULL en un índice único. Aquí los correos vacíos o compuestos solo por espacios se convierten en NULL. Si el correo es obligatorio, valida también que exista y sea válido. En un CRM multiempresa, sustituye la restricción del ejemplo por UNIQUE (empresa_id, email_normalizado) y usa un empresa_id obligatorio. En ese caso, incluye la empresa también al detectar duplicados y al revisar fusiones.
Ejecuta el ALTER TABLE por separado después de confirmar la limpieza. MySQL realiza una confirmación implícita al ejecutar esta operación. Durante la transición, pausa o coordina los registros, importaciones y demás escrituras sobre clientes para evitar que un duplicado se cuele entre la limpieza y la creación del índice. Verifica cuánto tarda el cambio y qué bloqueos puede tomar antes de aplicarlo en producción.
5. Maneja el duplicado en PHP y en las importaciones
La aplicación puede buscar primero una ficha para dar una respuesta rápida al usuario. Aun así, dos solicitudes simultáneas pueden comprobar el mismo correo antes de que alguna lo guarde. La restricción única resuelve esa carrera. Con PDO, revisa el código MySQL 1062 y confirma qué restricción falló, ya que otras claves únicas también pueden devolver ese código. En el registro público, muestra un mensaje genérico que no revele si existe una cuenta. En un panel interno autorizado, puedes dirigir al operador hacia la ficha existente.
Antes de cerrar la migración, prueba un correo con mayúsculas y espacios exteriores, un correo distinto y un correo ausente si está permitido. Verifica también que los pedidos de las fichas unificadas siguen disponibles y que el recuento coincide con el que registraste antes del cambio.
Comentarios (0)
Inicia sesión con tu cuenta para dejar un comentario.
Iniciar sesión para comentar