El síntoma: WHERE intenta usar un valor que todavía no se ha agregado
Las funciones COUNT, SUM, AVG, MIN y MAX resumen varias filas. WHERE, en cambio, decide qué filas participan antes de formar grupos y calcular los agregados. Por eso una condición como WHERE COUNT(*) > 2 no expresa una etapa válida en la consulta habitual.
PostgreSQL documenta esta diferencia de forma directa: WHERE selecciona filas antes de calcular grupos y agregados, mientras que HAVING selecciona grupos después de ese cálculo.
Ejemplo reproducible: contar categorías dentro de WHERE
WITH ventas(categoria, importe) AS (
VALUES ('A', 10), ('A', 20), ('A', 30),
('B', 5), ('B', 7)
)
SELECT categoria, COUNT(*) AS ventas
FROM ventas
WHERE COUNT(*) >= 3
GROUP BY categoria;La condición depende de COUNT(*), que solo existe después de agrupar. El problema no se resuelve moviendo palabras al azar: primero identifica si estás filtrando filas o grupos.
Usa HAVING cuando la condición depende del resultado agregado
WITH ventas(categoria, importe) AS (
VALUES ('A', 10), ('A', 20), ('A', 30),
('B', 5), ('B', 7)
)
SELECT categoria,
COUNT(*) AS ventas,
SUM(importe) AS total
FROM ventas
GROUP BY categoria
HAVING COUNT(*) >= 3;| categoria | ventas | total |
|---|---|---|
| A | 3 | 60 |
WHERE y HAVING pueden aparecer juntos
No son alternativas exclusivas. Usa WHERE para decidir qué filas entran al cálculo y HAVING para decidir qué grupos permanecen después.
SELECT categoria, COUNT(*) AS ventas_validas
FROM ventas
WHERE importe >= 10
GROUP BY categoria
HAVING COUNT(*) >= 2;Primero desaparecen las ventas de importe menor que 10. Después se forman los grupos con las filas restantes y solo se conservan los que alcanzan dos registros.
Método de diagnóstico
- Subraya la condición que provoca el error.
- Pregunta de qué depende: ¿de una columna de cada fila o de un agregado calculado sobre varias filas?
- Si depende de filas, usa WHERE.
- Si depende del grupo, usa HAVING y comprueba que el
GROUP BYtenga la granularidad que realmente necesitas.
Error frecuente: mover todo a HAVING
Que HAVING acepte una condición no significa que sea su mejor lugar
Una condición simple sobre filas, como importe >= 10, expresa mejor su intención en WHERE. Filtrar antes también evita que filas irrelevantes participen en la agrupación. Reserva HAVING para condiciones que realmente dependen del grupo o del agregado.
Práctica correctiva
Separa el filtro de filas del filtro de grupos
Quieres contar únicamente ventas de al menos 10 euros y mostrar solo categorías que tengan dos o más de esas ventas. ¿Dónde va cada condición?
Solución razonada
SELECT categoria, COUNT(*) AS ventas_validas
FROM ventas
WHERE importe >= 10
GROUP BY categoria
HAVING COUNT(*) >= 2;WHERE reduce las filas de entrada; HAVING evalúa el conteo de cada grupo ya formado.
Qué debes recordar
WHEREfiltra filas antes de agrupar.HAVINGfiltra grupos después de calcular agregados.- Una condición puede necesitar ambas etapas en la misma consulta.
- No muevas filtros de fila a HAVING solo para evitar un mensaje de error.