Agregación · detectar valores repetidos

Encontrar duplicados con GROUP BY y HAVING

Detecta valores o combinaciones repetidas, cuenta cuántas veces aparecen y distingue una repetición real de un duplicado provocado por un JOIN.

Lectura: 9–11 minEjemplos comprobadosPráctica con pistas
Contenido de esta guía

Respuesta rápida

Para encontrar valores repetidos, agrupa por la columna —o combinación de columnas— que define la repetición y filtra los grupos con HAVING COUNT(*) > 1.

Patrón básico
SELECT columna, COUNT(*) AS apariciones
FROM tabla
GROUP BY columna
HAVING COUNT(*) > 1;

El patrón no decide por sí solo si una repetición es un error. Primero debes definir qué columna o combinación representa una identidad que debería ser única.

Primero define qué significa «duplicado»

Dos filas pueden compartir una ciudad, una fecha o un estado sin ser duplicadas. La repetición solo es problemática cuando afecta a una clave de negocio que debería distinguir una entidad o un hecho.

Repetición frente a duplicado de negocio
Caso¿Es necesariamente un duplicado?Clave que revisarías
Dos clientes viven en MadridNoLa ciudad no identifica a un cliente
Dos contactos usan el mismo email cuando el email debe ser únicoSí, según esa reglaemail
Dos líneas repiten producto y pedido cuando esa pareja debe ser únicaProbablemente(pedido_id, producto_id)

La pregunta útil no es «¿hay filas iguales?», sino «¿qué columnas deberían identificar de forma única este dato?».

Ejemplo: localizar emails repetidos

Este conjunto pequeño contiene dos emails repetidos y un valor NULL. Como la regla de negocio solo considera emails conocidos, el ejemplo excluye NULL antes de agrupar.

SQL comprobado en SQLite
WITH contactos(id, email) AS (
  VALUES
    (1, 'ana@example.test'),
    (2, 'luis@example.test'),
    (3, 'ana@example.test'),
    (4, 'marta@example.test'),
    (5, 'luis@example.test'),
    (6, NULL)
)
SELECT email, COUNT(*) AS apariciones
FROM contactos
WHERE email IS NOT NULL
GROUP BY email
HAVING COUNT(*) > 1
ORDER BY email;
Resultado comprobado
emailapariciones
ana@example.test2
luis@example.test2

GROUP BY email forma un grupo por valor. COUNT(*) cuenta sus filas y HAVING conserva únicamente los grupos con más de una aparición.

Duplicados definidos por varias columnas

Si la identidad depende de más de una columna, agrupa por todas ellas. Por ejemplo, para detectar una combinación repetida de pedido y producto:

Clave compuesta
SELECT pedido_id,
       producto_id,
       COUNT(*) AS apariciones
FROM detalle_pedido
GROUP BY pedido_id, producto_id
HAVING COUNT(*) > 1;

Agrupar solo por producto_id respondería otra pregunta: cuántas líneas usan cada producto en todos los pedidos. El nivel de agrupación debe coincidir con la regla de unicidad que quieres comprobar.

Si necesitas practicar esta idea con más detalle, consulta GROUP BY con varias columnas.

Cómo recuperar las filas originales duplicadas

La consulta agregada devuelve la clave repetida y su recuento, no cada fila original. Para inspeccionar las filas completas, calcula primero las claves repetidas y vuelve a unirlas con el origen.

Diagnóstico en dos pasos
WITH repetidos AS (
  SELECT email
  FROM contactos
  WHERE email IS NOT NULL
  GROUP BY email
  HAVING COUNT(*) > 1
)
SELECT c.id, c.email
FROM contactos AS c
JOIN repetidos AS r
  ON r.email = c.email
ORDER BY c.email, c.id;

Este segundo paso es especialmente útil antes de corregir datos: primero identifica la clave problemática y después revisa exactamente qué filas están involucradas.

Qué hacer con NULL

En una agrupación, los valores NULL pueden aparecer juntos como un grupo. Eso no significa automáticamente que varios datos desconocidos representen el mismo valor de negocio.

Si un campo opcional puede ser NULL, decide explícitamente si quieres incluirlo en el diagnóstico. En muchas búsquedas de emails, teléfonos o códigos repetidos resulta más útil añadir WHERE columna IS NOT NULL. Si la regla de negocio considera NULL relevante, no lo filtres.

Errores frecuentes

Usar DISTINCT para «buscar» duplicados

DISTINCT elimina repeticiones de la salida, pero no te dice qué valor estaba repetido ni cuántas veces apareció. Para diagnosticar, conserva el recuento con GROUP BY y HAVING.

Escribir COUNT(*) en WHERE

WHERE filtra filas antes de que existan los grupos. Una condición sobre el recuento pertenece a HAVING.

Confundir duplicados del dato con filas multiplicadas por un JOIN

Una relación uno-a-muchos puede repetir columnas de la tabla izquierda sin que los datos estén duplicados. Si el problema aparece solo después de unir tablas, revisa por qué un JOIN multiplica filas.

Agrupar por demasiadas columnas

Si incluyes un identificador único como id, cada fila puede formar su propio grupo y ocultar la repetición que querías detectar. Agrupa solo por la clave de negocio que estás comprobando.

Práctica guiada

Intermedia · agregación

Encuentra clientes con varios pedidos

En la tabla pedidos, devuelve los cliente_id que aparecen al menos dos veces. Muestra también el número de pedidos y ordena primero por el recuento descendente y después por cliente_id.

Criterio de éxito: aparecen seis clientes; los clientes 1 y 3 tienen tres pedidos cada uno.

Compatibilidad entre motores

La combinación básica de GROUP BY, COUNT y HAVING está disponible en PostgreSQL y SQLite y forma parte del patrón habitual para filtrar grupos después de agregarlos. Las reglas sobre columnas no agrupadas y extensiones de agregación cambian entre motores, por lo que conviene mantener la consulta explícita.

El criterio de duplicado pertenece al modelo de datos: SQL puede contar repeticiones, pero no puede adivinar qué columnas deberían ser únicas para tu negocio.

Qué debes recordar

  • Define primero la clave que debería ser única.
  • Agrupa por esa clave y cuenta filas.
  • Usa HAVING COUNT(*) > 1 para conservar solo las repeticiones.
  • Con varias columnas, agrupa por toda la combinación.
  • DISTINCT oculta repeticiones; no las diagnostica.
  • Un JOIN puede multiplicar filas sin que exista un duplicado almacenado.

Conceptos relacionados

Fuentes técnicas consultadas