En el artículo anterior aprendimos a agrupar datos con GROUP BY. Ahora somos capaces de calcular, por ejemplo, el total de ventas por cada empleado.
Pero, ¿qué pasa si queremos ir un paso más allá y preguntar:
Dime qué empleados han vendido menos de 10.000€
Nuestra intuición nos lleva a usar el WHERE, que es la herramienta de filtrado que conocemos.
-- ⛔ ESTO DARÁ ERROR
SELECT EmpleadoID, SUM(Ventas)
FROM Pedidos
WHERE SUM(Ventas) > 10000 -- ¡Error!
GROUP BY EmpleadoID;
SQL Server nos lanzará un error. ¿Por qué? Porque el WHERE se ejecuta antes de agrupar.
Porque en ese punto, el motor todavía no ha calculado cuanto es la SUM(Ventas) de nadie.
Para filtrar basándonos en el resultado de una agregación, necesitamos la cláusula HAVING.
La cláusula HAVING
HAVING funciona exactamente igual que el WHERE, pero se aplica después de haber creado los grupos y calculado sus agregados.
La sintaxis correcta para el ejemplo anterior sería:
SELECT EmpleadoID, SUM(Ventas) as TotalVentas
FROM Pedidos
GROUP BY EmpleadoID
HAVING SUM(Ventas) > 10000;
Ahora sí:
- SQL Server agrupa las ventas por empleado.
- Calcula la suma para cada uno.
- Luego, descarta los grupos que no superan los 10.000.
WHERE vs HAVING: La gran diferencia
Confundir estos dos es uno de los errores más comunes. La diferencia radica en cuándo se aplican durante el procesamiento de la consulta.
El orden de ejecución es:
FROM: Carga la tabla.
WHERE: Filtra las filas individuales (antes de agrupar).
GROUP BY: Crea los grupos con las filas que pasaron el filtro.
HAVING: Filtra los grupos completos (basándose en agregados).
SELECT: Muestra las columnas.
- ¿Filtras por una columna original de la tabla? Usa WHERE.
- ¿Filtras por un cálculo (
COUNT,SUM,AVG)? Usa HAVING.
Combinando WHERE y HAVING
WHERE y HAVING no son enemigos. Se pueden (suelen) usar a la vez. Para dominar esto, veamos un ejemplo donde usamos ambos a la vez.
Por ejemplo, queremos saber
Categorías de Productos han generado más de 5.000€ en ventas
pero solo considerando las ventas del año 2023.
Traducido en “SQLiense” esto sería algo asi,
SELECT Categoria, SUM(Importe) as TotalVentas
FROM Ventas
WHERE Anio = 2023 -- 1. Filtramos filas (solo año 2023)
GROUP BY Categoria -- 2. Agrupamos lo que queda
HAVING SUM(Importe) > 5000; -- 3. Filtramos los grupos resultantes
- El
WHEREelimina los datos antiguos (2022, 2021…) antes de que el motor pierda tiempo agrupándolos y sumándolos. - El
HAVINGsolo se ocupa de comprobar el total final.
Técnicamente podrías poner condiciones de fila en el HAVING (como HAVING Anio = 2023 si agrupas por Año). Pero es una mala práctica.
Obligas a SQL Server a agrupar datos que luego vas a descartar. Filtra siempre lo antes posible: usa WHERE para todo lo que puedas.
Múltiples condiciones en HAVING
Al igual que en el WHERE, podemos usar operadores lógicos AND y OR en el HAVING.
SELECT Departamento, COUNT(*) as NumEmpleados, AVG(Salario) as SalarioMedio
FROM Empleados
GROUP BY Departamento
HAVING COUNT(*) > 10 -- Departamentos grandes
AND AVG(Salario) < 30000; -- Y con salario medio bajo
