HAVING

La cláusula HAVING filtra grupos creados por GROUP BY, igual que WHERE filtra filas individuales. La diferencia clave es que WHERE actúa antes de la agrupación (sobre filas) y HAVING actúa después (sobre grupos). Esto significa que HAVING puede usar funciones de agregación como COUNT, SUM o AVG, cosa que WHERE no puede.

Sintaxis

SELECT columnas, funcion_agregacion(columna)
FROM tabla
WHERE condicion_filas
GROUP BY columna
HAVING condicion_grupos
ORDER BY columna;

HAVING se escribe después de GROUP BY y antes de ORDER BY.

Filtrar por conteo

El uso más común de HAVING es filtrar grupos según el resultado de una función de agregación. Categorías que tengan más de 2 productos:

SELECT
    c.nombre AS categoria,
    COUNT(*) AS total_productos
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
GROUP BY c.nombre
HAVING total_productos > 2
ORDER BY total_productos DESC;
categoriatotal_productos
Smartphones4
Portátiles3
Accesorios electrónicos3
Muebles3
Fitness3
Programación3
Novelas3

De las 11 categorías con productos, solo 7 tienen más de 2. Las categorías Camisetas, Pantalones, Cocina y Running (con 2 productos cada una) se filtran.

Observa que usamos el alias total_productos en HAVING. MySQL permite esto porque HAVING se procesa después de SELECT.

HAVING con SUM

Clientes cuyo gasto total supera los 500 euros:

SELECT
    cl.nombre,
    cl.apellidos,
    COUNT(p.id) AS pedidos,
    SUM(p.total) AS gasto_total
FROM clientes cl
JOIN pedidos p ON cl.id = p.cliente_id
GROUP BY cl.id, cl.nombre, cl.apellidos
HAVING gasto_total > 500
ORDER BY gasto_total DESC;
nombreapellidospedidosgasto_total
PedroFernández Castro21979.97
MaríaGarcía López31849.96
CarlosRodríguez Martín21029.98
CarmenRuiz Jiménez11399.00
JavierMoreno Díaz1599.99
DavidSánchez Moreno2509.97

Primero agrupamos todos los pedidos por cliente, después filtramos los grupos cuya suma de totales supera los 500 euros. Pedro lidera con casi 1980 euros en 2 pedidos.

HAVING con AVG

Categorías cuyo precio medio supera los 100 euros:

SELECT
    c.nombre AS categoria,
    ROUND(AVG(p.precio), 2) AS precio_medio,
    COUNT(*) AS productos
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
GROUP BY c.nombre
HAVING precio_medio > 100
ORDER BY precio_medio DESC;
categoriaprecio_medioproductos
Portátiles1616.003
Smartphones874.744
Muebles382.663
Cocina144.992
Running139.992

Solo 5 de las 11 categorías tienen un precio medio superior a 100 euros.

WHERE vs HAVING

La diferencia fundamental:

  • WHERE filtra filas antes de agrupar. No puede usar funciones de agregación.
  • HAVING filtra grupos después de agrupar. Puede usar funciones de agregación.

Puedes usarlos juntos en la misma consulta. Cada uno hace su trabajo en un momento diferente:

SELECT
    estado,
    COUNT(*) AS total,
    ROUND(AVG(total), 2) AS media
FROM pedidos
WHERE estado != 'cancelado'
GROUP BY estado
HAVING total >= 4
ORDER BY total DESC;
estadototalmedia
pendiente7307.70
entregado7692.84
enviado4509.74

El flujo es:

  1. WHERE estado != 'cancelado': elimina los 3 pedidos cancelados (quedan 22).
  2. GROUP BY estado: agrupa los 22 pedidos en 4 grupos (pendiente, procesando, enviado, entregado).
  3. HAVING total >= 4: filtra los grupos con menos de 4 pedidos. "procesando" tiene solo 4, así que pasa. Veamos... en realidad "procesando" tiene 4, así que sí pasa el filtro >= 4.

Un error común es usar WHERE donde debería ir HAVING:

-- ERROR: no puedes usar funciones de agregación en WHERE
SELECT estado, COUNT(*) AS total
FROM pedidos
WHERE COUNT(*) > 3
GROUP BY estado;
ERROR 1111 (HY000): Invalid use of group function

Las funciones de agregación solo tienen sentido después de la agrupación, y WHERE se ejecuta antes.

HAVING sin GROUP BY

Técnicamente, puedes usar HAVING sin GROUP BY. En ese caso, MySQL trata toda la tabla como un solo grupo:

SELECT
    COUNT(*) AS total_productos,
    AVG(precio) AS precio_medio
FROM productos
HAVING total_productos > 20;
total_productosprecio_medio
30339.6567

Como hay 30 productos (mayor que 20), la condición se cumple y se muestra el resultado. Si hubiera 20 o menos, el resultado estaría vacío. Aunque esto es válido, en la práctica casi siempre usarás HAVING junto con GROUP BY.

HAVING con múltiples condiciones

Puedes combinar varias condiciones en HAVING con AND y OR:

SELECT
    c.nombre AS categoria,
    COUNT(*) AS total,
    ROUND(AVG(p.precio), 2) AS precio_medio,
    SUM(p.stock) AS stock
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
GROUP BY c.nombre
HAVING total >= 3
   AND precio_medio < 100
ORDER BY stock DESC;
categoriatotalprecio_mediostock
Accesorios electrónicos335.32650
Fitness383.32320
Novelas314.99265
Programación334.99157

Categorías con al menos 3 productos y un precio medio inferior a 100 euros. Ambas condiciones deben cumplirse para que el grupo aparezca en el resultado.

Orden de procesamiento completo

Con HAVING en la mezcla, el orden completo de procesamiento queda:

  1. FROM / JOIN: identifica y une tablas.
  2. WHERE: filtra filas individuales.
  3. GROUP BY: agrupa las filas restantes.
  4. HAVING: filtra grupos.
  5. SELECT: evalúa expresiones y alias.
  6. ORDER BY: ordena el resultado.
  7. LIMIT: restringe filas.

Practica con HAVING

Usa el editor para filtrar grupos con HAVING:

Simulador SQL
Ctrl+Enter para ejecutar

En el siguiente artículo veremos patrones comunes de HAVING con COUNT para encontrar duplicados, valores únicos y distribuciones.

Escrito por Eduardo Lázaro