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.
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.
| Caso | ¿Es necesariamente un duplicado? | Clave que revisarías |
|---|---|---|
| Dos clientes viven en Madrid | No | La ciudad no identifica a un cliente |
| Dos contactos usan el mismo email cuando el email debe ser único | Sí, según esa regla | email |
| Dos líneas repiten producto y pedido cuando esa pareja debe ser única | Probablemente | (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.
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;| apariciones | |
|---|---|
| ana@example.test | 2 |
| luis@example.test | 2 |
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:
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.
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
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.
cliente_id.COUNT(*).HAVING COUNT(*) >= 2.Solución razonada
SELECT cliente_id, COUNT(*) AS pedidos
FROM pedidos
GROUP BY cliente_id
HAVING COUNT(*) >= 2
ORDER BY pedidos DESC, cliente_id;| cliente_id | pedidos |
|---|---|
| 1 | 3 |
| 3 | 3 |
| 2 | 2 |
| 5 | 2 |
| 7 | 2 |
| 8 | 2 |
La consulta detecta valores repetidos de cliente_id. En este caso no son datos erróneos: varios pedidos por cliente son válidos. El mismo patrón sirve para detectar una clave que sí debería ser única.
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(*) > 1para conservar solo las repeticiones. - Con varias columnas, agrupa por toda la combinación.
DISTINCToculta repeticiones; no las diagnostica.- Un JOIN puede multiplicar filas sin que exista un duplicado almacenado.