Back to code SQL

Encontrar clientes duplicados y quedarse con el más reciente

Una función de ventana numera los registros repetidos; los que quedan con puesto mayor a uno son los que sobran.

This post is not in your language yet; you are seeing it in Spanish. The code is the same.

The problem

La tabla de clientes tiene el mismo correo cargado varias veces, con mayúsculas y espacios distintos. Hay que saber cuáles sobran antes de borrar nada.

SQL
-- Clientes repetidos por correo: quedarse con el registro más reciente.
WITH ordenados AS (
  SELECT id, correo, nombre, actualizado_en,
         ROW_NUMBER() OVER (
           PARTITION BY LOWER(TRIM(correo))
           ORDER BY actualizado_en DESC, id DESC
         ) AS puesto
  FROM clientes
)
SELECT id, correo, nombre, actualizado_en
FROM ordenados
WHERE puesto > 1          -- estos son los que sobran
ORDER BY correo;

-- Cuando el SELECT muestre lo esperado, el borrado es el mismo criterio:
-- DELETE FROM clientes WHERE id IN (SELECT id FROM ordenados WHERE puesto > 1);

Why it is written this way

  1. PARTITION BY arma un grupo por cada correo normalizado: sin mayúsculas y sin espacios alrededor.
  2. ROW_NUMBER numera dentro de cada grupo empezando por el más reciente; el puesto 1 es el que se queda.
  3. El desempate por id evita que dos registros con la misma fecha salgan en orden distinto cada vez.
  4. Primero el SELECT, después el DELETE: el mismo criterio, mirado antes de ejecutarlo.
How to try it

Funciona en PostgreSQL, MySQL 8, SQL Server y SQLite 3.25 o superior. Corré primero solo el SELECT sobre una copia de la tabla.

Watch out

Borrar el duplicado deja huérfanos sus pedidos, facturas o lo que apunte a ese id. Antes del DELETE hay que reasignar esas referencias al registro que se queda.

Adapt it to your case with AI

The button copies this prompt with the code pasted below it. Replace the brackets with your own details and paste it into ChatGPT, Claude, Gemini or Copilot.

The prompt
Tengo esta consulta SQL que detecta clientes duplicados por correo. Mi motor es [MOTOR Y VERSIÓN]. Extendela para que antes de borrar reasigne al registro que se queda las filas de estas tablas que apuntan al duplicado: [TABLAS Y COLUMNAS]. Todo dentro de una transacción, con un SELECT de control antes y después. El criterio de duplicado es: [CRITERIO].

Example written and reviewed by hand.

Share

Comments

No comments yet. Yours can be the first.

Plain text only, no links. No email is requested. Comments are reviewed before they are published.

Did you adapt it to something of yours?

Tell me what you solved with it, or what it was missing.

Keep trying

More code

See all
Darinel Ortega Online · I reply during the day
Today
Hello. I'm not selling anything here: this is for exchanging knowledge about technology.
Write whatever you like — you can send text, images or documents. Messages reach my console and I reply from there.
You are writing outside working hours. I will read you as soon as the day starts.

An open conversation to share knowledge. Messages reach my console and I reply from there.

Let us book a conversation

Pick the day and time that work for you. Thirty minutes, no sales pitch.

Video call

For a video call, just ask for one here and I'll send you the session link.