WHERE vs HAVING en SQL: cuándo filtrar antes y cuándo después de agrupar
Escribes WHERE COUNT(*) >= 2 y SQL te responde con un error. Cambias WHERE por HAVING y funciona. Parece una regla arbitraria, pero tiene una razón: los dos filtran en momentos distintos de la consulta. Aquí lo ves con ejemplos que se ejecutan, con una base de ejemplo del blog.
¿En qué orden ejecuta SQL una consulta?
El orden en que escribes una consulta no es el orden en que SQL la resuelve. Este es el orden lógico:
| Paso | Cláusula | Qué hace |
|---|---|---|
| 1 | FROM y JOIN | Arma el conjunto de filas de partida |
| 2 | WHERE | Descarta filas individuales |
| 3 | GROUP BY | Agrupa las filas que quedaron |
| 4 | HAVING | Descarta grupos completos |
| 5 | SELECT | Calcula las columnas que se devuelven |
| 6 | ORDER BY y LIMIT | Ordena y recorta |
Ahí está la razón de todo: cuando corre WHERE, los grupos todavía no existen, así que no hay un COUNT por grupo contra el cual comparar. HAVING corre después de agrupar, cuando ya hay un conteo por grupo.
Primero, los datos
La base de ejemplo tiene 14 pedidos. Cuántos hay por estado:
Hay 3 cancelados y 11 completados. Vamos a trabajar con los completados.
WHERE: filtra filas antes de agrupar
Cuenta los pedidos completados de cada cliente. El WHERE descarta primero los cancelados, y recién después se agrupa:
Salen 7 grupos. El primero tiene customer_id en NULL: es el pedido 114, que se registró sin cliente. GROUP BY trata a los NULL como un grupo propio.
HAVING: filtra grupos después de agrupar
Ahora queremos solo los clientes con al menos 2 pedidos completados. Esa condición habla del resultado de COUNT(*), no de una fila, así que va en HAVING:
Quedan los clientes 1, 3 y 8, con 3, 2 y 2 pedidos. Los demás grupos de la celda anterior se descartaron completos.
¿Por qué da error si uso COUNT en el WHERE?
Si intentas lo mismo con WHERE:
SELECT customer_id, COUNT(*) AS pedidos
FROM orders
WHERE status = 'completada' AND COUNT(*) >= 2
GROUP BY customer_id;SQLite responde misuse of aggregate: COUNT(). Otros motores usan mensajes distintos, pero el motivo es el mismo: en el paso del WHERE todavía no hay grupos, así que COUNT(*) no tiene sentido ahí.
HAVING con SUM y la trampa de los NULL
HAVING funciona con cualquier agregado. Veamos los clientes que gastaron más de 200 en pedidos completados:
Salen los clientes 1 y 3, con 400 cada uno. Hay un detalle: el cliente 6 tiene un pedido completado, pero su total_amount es NULL, así que su SUM es NULL, y NULL > 200 no es verdadero. Ese grupo queda fuera sin avisar. Si los NULL importan en tu análisis, revísalos antes de filtrar.
¿Puedo usar WHERE y HAVING en la misma consulta?
Sí, y es lo habitual: WHERE para las condiciones sobre filas y HAVING para las condiciones sobre grupos. Cada uno hace su trabajo:
El WHERE quita los pedidos cancelados y el pedido sin cliente. El HAVING deja solo a quienes tienen al menos 2 pedidos y más de 300 en total.
¿Cuándo uso WHERE y cuándo HAVING?
| Tu condición habla de... | Usa |
|---|---|
Una columna de cada fila (status, country, order_date) | WHERE |
Un resultado agregado (COUNT, SUM, AVG, MAX) | HAVING |
| Ambas cosas | WHERE para las filas y HAVING para los grupos |
Una regla útil: si una condición se puede poner en el WHERE, ponla ahí. Filtrar filas antes de agrupar suele dejar menos datos para procesar y hace la consulta más clara.
¿Qué te preguntan sobre esto en una entrevista de SQL?
La pregunta típica es "¿cuál es la diferencia entre WHERE y HAVING?". La respuesta corta que busca el entrevistador es el orden de ejecución: WHERE va antes de agrupar y no admite agregados, HAVING va después y sí. Si además das un ejemplo de los dos juntos, como el de la celda 5, ya respondiste mejor que la mayoría.
Inténtalo tú mismo
Escribe una consulta que muestre el nombre de los clientes que gastaron más de 150 en pedidos completados, junto con ese total. Llama total a la columna del gasto y ordena por nombre.
FUENTES Y VERIFICACIÓN
Todas las consultas de este artículo se ejecutaron contra la base de ejemplo del blog antes de publicarse. Referencias: documentación oficial de SQLite sobre SELECT (cláusulas WHERE, GROUP BY y HAVING) y sobre funciones de agregado; estándar SQL, orden lógico de evaluación de una consulta. Revisado por Pedro Rodríguez. Se revisa cada trimestre.
Deja tu correo y te llega la invitación al Discord y el newsletter semanal con lo nuevo en datos e IA.
Solo lo usamos para el enlace al Discord y el newsletter. Puedes darte de baja cuando quieras.