HAVING: filtrar grupos
BásicoWHERE filtra filas antes de agrupar. HAVING filtra grupos después de agregar. Una condición sobre count, sum o avg solo puede ir en HAVING; una condición sobre columnas de la fila conviene ponerla en WHERE.
Sintaxis
SELECT columna, count(*)
FROM tabla
WHERE condicion_por_fila
GROUP BY columna
HAVING count(*) >= n;Ejemplo
SELECT
k.user_id,
u.full_name,
count(*) AS rechazos,
to_char(max(k.event_at), 'YYYY-MM-DD') AS ultimo_rechazo
FROM kyc_events AS k
INNER JOIN users AS u ON u.id = k.user_id
WHERE k.outcome = 'rejected'
AND u.country = 'UY'
GROUP BY k.user_id, u.full_name
HAVING count(*) >= 2
ORDER BY rechazos DESC, k.user_id;Resultado
| user_id | full_name | rechazos | ultimo_rechazo |
|---|---|---|---|
| 921 | Mariana García | 2 | 2025-04-20 |
| 1007 | Catalina Chávez | 2 | 2025-12-03 |
| 2600 | Gabriel Rodríguez | 2 | 2025-09-21 |
| 3901 | Rodrigo Vega | 2 | 2025-11-04 |
Cómo leerlo
Son las personas de Uruguay con dos o más intentos rechazados de verificación de identidad (KYC, Know Your Customer). El WHERE deja solo los eventos rechazados de usuarios uruguayos, y después el HAVING descarta a quienes tuvieron un único rechazo.
Error común
Así no
SELECT k.user_id, count(*) AS rechazos
FROM kyc_events AS k
GROUP BY k.user_id
HAVING count(*) >= 2 AND k.outcome = 'rejected';Poner en HAVING una condición sobre la fila. Aquí además falla: outcome no está agrupado. Y aunque funcionara, contaría también los intentos aprobados. El filtro por fila (outcome = 'rejected') va en WHERE, antes de contar; en HAVING solo queda la condición sobre el agregado.
En otros motores: MySQL, BigQuery
MySQLBigQuery
Aceptan el alias del
SELECTenHAVING(HAVING rechazos >= 2). PostgreSQL y SQL Server no: hay que repetircount(*).
Estos fragmentos no se ejecutan en el curso: se muestran como referencia de cada motor.