En SQL, la cláusula HAVING se utiliza para filtrar los resultados de un GROUP BY, es decir, aplicar condiciones sobre grupos en lugar de sobre filas individuales.
Mientras que WHERE filtra filas antes de agrupar, HAVING actúa después de que las filas ya han sido agrupadas y se han calculado funciones de agregación como SUM(), COUNT(), AVG(), MAX() o MIN().
👉 Si pensás en HAVING como el “WHERE de los grupos”, nunca más te vas a confundir.
Sintaxis básica de HAVING
SELECT columna1, funcion_agregada(columna2)
FROM tabla
WHERE condiciones_filas
GROUP BY columna1
HAVING condiciones_grupo
ORDER BY columna1;
- WHERE: filtra filas individuales.
- GROUP BY: agrupa filas por columnas.
- HAVING: filtra los grupos resultantes.
- ORDER BY: ordena la salida final.
Ejemplos prácticos de HAVING
1) Clientes con más de 5 pedidos
SELECT cliente_id, COUNT(*) AS total_pedidos
FROM pedidos
GROUP BY cliente_id
HAVING COUNT(*) > 5;
👉 Devuelve solo los clientes que tienen más de 5 pedidos.
2) Departamentos con salario promedio mayor a 3000
SELECT departamento, AVG(salario) AS salario_promedio
FROM empleados
GROUP BY departamento
HAVING AVG(salario) > 3000;
👉 Filtra únicamente los departamentos con sueldos altos.
3) Países con más de 100 clientes activos
SELECT pais, COUNT(*) AS clientes_activos
FROM clientes
WHERE activo = 1
GROUP BY pais
HAVING COUNT(*) > 100;
👉 Primero se filtran solo clientes activos (WHERE). Luego, HAVING se encarga de mostrar países con más de 100.
4) Productos con ventas superiores a 10.000
SELECT producto_id, SUM(total) AS ventas
FROM pedidos
GROUP BY producto_id
HAVING SUM(total) > 10000;
5) Varias condiciones en HAVING
SELECT departamento, COUNT(*) empleados, AVG(salario) promedio
FROM empleados
GROUP BY departamento
HAVING COUNT(*) > 10 AND AVG(salario) > 2500;
👉 Devuelve departamentos con más de 10 empleados y salario medio superior a 2500.
Diferencias clave: WHERE vs HAVING
| Característica | WHERE | HAVING |
|---|---|---|
| Momento | Antes de agrupar | Después de agrupar |
| Se aplica a | Filas individuales | Grupos de filas |
| Admite agregados | ❌ No | ✅ Sí |
| Ejemplo | WHERE salario > 2000 | HAVING AVG(salario) > 2000 |
👉 Regla práctica:
- Si filtrás filas, usá
WHERE. - Si filtrás resultados de agregaciones, usá
HAVING.
HAVING sin GROUP BY
En la mayoría de motores se puede usar HAVING incluso sin GROUP BY, actuando como un filtro de toda la tabla tratada como un único grupo.
Ejemplo:
SELECT SUM(total) AS ventas_totales
FROM pedidos
HAVING SUM(total) > 50000;
👉 Devuelve el total de ventas solo si supera 50.000.
Errores comunes con HAVING
- Usar HAVING cuando corresponde WHERE
-- ❌ Mal: HAVING usado para filtrar filas
SELECT * FROM empleados
GROUP BY departamento
HAVING salario > 2000;
👉 Error, porque salario no está en función agregada ni en GROUP BY.
✅ Solución:
SELECT * FROM empleados
WHERE salario > 2000;
- Olvidar agrupar todas las columnas
-- ❌ Error en motores estrictos
SELECT departamento, nombre, COUNT(*)
FROM empleados
GROUP BY departamento
HAVING COUNT(*) > 5;
👉 nombre no está ni en GROUP BY ni en una función de agregación.
- Confundir COUNT(*) con COUNT(columna)
COUNT(*): cuenta todas las filas del grupo.COUNT(columna): ignora filas conNULLen esa columna.
- Creer que HAVING mejora performance
👉 En realidad, el filtrado enHAVINGse hace después delGROUP BY, cuando ya se procesó todo. Si podés aplicar filtros previos, usáWHEREpara reducir filas antes.
Buenas prácticas con HAVING
- Filtrar primero lo que puedas con
WHERE. - Usar alias para funciones agregadas y reutilizarlos en
HAVING(en motores que lo soportan). - Combinar condiciones con
AND/ORpara expresiones complejas. - Evitar agrupar más columnas de las necesarias: cada columna extra aumenta la cardinalidad del
GROUP BY. - Usar
HAVINGpara métricas de negocio: top clientes, ventas mínimas, umbrales.
Ejemplo avanzado: top clientes por región
SELECT c.region, c.nombre, SUM(p.total) AS ventas
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
WHERE p.fecha >= '2025-01-01'
GROUP BY c.region, c.nombre
HAVING SUM(p.total) > 1000
ORDER BY c.region, ventas DESC;
👉 Devuelve clientes que superaron los 1000 en ventas durante 2025, agrupados por región.
HAVING con ROLLUP y CUBE
Cuando usás funciones avanzadas de agrupación, HAVING también sirve para filtrar los subtotales.
Ejemplo en MySQL con ROLLUP:
SELECT departamento, puesto, SUM(salario) total
FROM empleados
GROUP BY departamento, puesto WITH ROLLUP
HAVING SUM(salario) > 10000;
👉 Solo muestra grupos y subtotales cuyo total supere 10.000.
Preguntas frecuentes (FAQ)
1. ¿HAVING siempre necesita GROUP BY?
No. Si no hay GROUP BY, toda la tabla se trata como un grupo único.
2. ¿Puedo usar columnas no agregadas en HAVING?
Depende del motor. En general, sí si están también en el GROUP BY.
3. ¿HAVING reemplaza WHERE?
No. Son complementarios. Usá WHERE para filas, HAVING para grupos.
4. ¿HAVING afecta performance?
Sí: se evalúa después del GROUP BY. Si podés filtrar antes con WHERE, es más eficiente.
5. ¿HAVING puede usarse con múltiples funciones de agregación?
Sí, y podés combinarlas con operadores lógicos (AND, OR).
Conclusión
La cláusula HAVING en SQL es la forma estándar de filtrar resultados agrupados después de aplicar funciones de agregación. Es esencial en reportes de negocio y análisis de datos, porque permite trabajar con métricas y aplicar condiciones sobre ellas.
La clave está en entender la diferencia con WHERE:
- WHERE → filas.
- HAVING → grupos.
Dominar HAVING junto con GROUP BY te permitirá crear consultas más potentes y profesionales.
Profundiza en filtros sobre agregados en GROUP BY y HAVING y vuelve al Glosario SQL completo.