filtrado-grupos-having-sql

Filtrado de grupos con HAVING en SQL

  • 3 min

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€

Nadie va a despedir a nadie 🙄

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;
Copied!

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;
Copied!

Ahora sí:

  1. SQL Server agrupa las ventas por empleado.
  2. Calcula la suma para cada uno.
  3. 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
Copied!
  • El WHERE elimina los datos antiguos (2022, 2021…) antes de que el motor pierda tiempo agrupándolos y sumándolos.
  • El HAVING solo 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

Copied!