Para quedarte solo con los grupos cuyo promedio cumple una condición, usa HAVING después del GROUP BY: GROUP BY cliente HAVING AVG(total) > 1000. WHERE no sirve para eso porque se aplica a cada fila antes de agrupar, cuando el promedio todavía no existe. Para listar las filas que superan el promedio general, compara con una subconsulta: WHERE total > (SELECT AVG(total) FROM pedidos).
Sintaxis
-- grupos cuyo promedio cumple una condición
SELECT grupo, AVG(valor) FROM tabla
GROUP BY grupo
HAVING AVG(valor) > limite;
-- filas por encima del promedio de toda la tabla
SELECT * FROM tabla
WHERE valor > (SELECT AVG(valor) FROM tabla);
-- filas por encima del promedio de su propio grupo
SELECT * FROM (
SELECT t.*, AVG(valor) OVER (PARTITION BY grupo) AS prom_grupo
FROM tabla t
) x
WHERE valor > prom_grupo;
WHERE frente a HAVING
WHERE | HAVING | |
|---|---|---|
| Se aplica a | Cada fila | Cada grupo |
| Momento | Antes de GROUP BY | Después de GROUP BY |
Puede usar AVG, SUM, COUNT | No | Sí |
| Cambia el promedio calculado | Sí: quita filas del cálculo | No: solo oculta grupos |
Paso a paso
- Escribe la consulta con
GROUP BYyAVGy comprueba que los promedios sean correctos. - Si quieres excluir filas del cálculo (pedidos cancelados, montos de prueba), ponlo en
WHERE. - Si quieres ocultar grupos según su promedio o su tamaño, agrega
HAVINGcon la expresión completa,AVG(total). - Ordena al final con
ORDER BY.
Tabla de ejemplo:
CREATE TABLE pedidos (
id INTEGER PRIMARY KEY,
cliente VARCHAR(30),
ciudad VARCHAR(30),
total DECIMAL(10, 2)
);
INSERT INTO pedidos (cliente, ciudad, total) VALUES
('Ferretería Luna', 'Puebla', 850.00),
('Ferretería Luna', 'Puebla', 1220.00),
('Ferretería Luna', 'Puebla', 930.00),
('Abarrotes Sol', 'Monterrey', 310.00),
('Abarrotes Sol', 'Monterrey', 295.00),
('Papelería Río', 'Guadalajara', 2400.00),
('Papelería Río', 'Guadalajara', 180.00),
('Café Norte', 'Monterrey', 1600.00);
Ejemplos resueltos
Ejemplo 1: clientes con ticket promedio mayor a 1,000
SELECT cliente,
ROUND(AVG(total), 2) AS ticket_promedio,
COUNT(*) AS pedidos
FROM pedidos
GROUP BY cliente
HAVING AVG(total) > 1000
ORDER BY ticket_promedio DESC;
| cliente | ticket_promedio | pedidos |
|---|---|---|
| Café Norte | 1600.0 | 1 |
| Papelería Río | 1290.0 | 2 |
Ferretería Luna queda fuera: su promedio es exactamente 1,000 y la condición pide mayor estricto. Café Norte entra con un solo pedido; si no quieres promedios basados en una sola fila, agrega AND COUNT(*) >= 2 al HAVING. Por portabilidad, repite la expresión AVG(total) en el HAVING: PostgreSQL y SQL Server no aceptan ahí el alias ticket_promedio (MySQL y SQLite sí).
Ejemplo 2: el mismo filtro en WHERE cambia el resultado
Compara el promedio por ciudad con y sin excluir los pedidos menores a 300:
SELECT ciudad, ROUND(AVG(total), 2) AS promedio
FROM pedidos
GROUP BY ciudad;
SELECT ciudad, ROUND(AVG(total), 2) AS promedio
FROM pedidos
WHERE total >= 300
GROUP BY ciudad;
| ciudad | sin WHERE | con WHERE total >= 300 |
|---|---|---|
| Guadalajara | 1290.0 | 2400.0 |
| Monterrey | 735.0 | 955.0 |
| Puebla | 1000.0 | 1000.0 |
WHERE quitó el pedido de 180 de Guadalajara y el de 295 de Monterrey antes de promediar, así que los promedios cambian. Un HAVING nunca cambia un promedio: solo decide qué grupos se muestran.
Ejemplo 3: pedidos por encima del promedio general y de su ciudad
El promedio de los 8 pedidos es 7,785 / 8 = 973.125. Los que lo superan:
SELECT id, cliente, total
FROM pedidos
WHERE total > (SELECT AVG(total) FROM pedidos)
ORDER BY total DESC;
| id | cliente | total |
|---|---|---|
| 6 | Papelería Río | 2400 |
| 8 | Café Norte | 1600 |
| 2 | Ferretería Luna | 1220 |
Para comparar cada pedido con el promedio de su propia ciudad, calcula ese promedio en cada fila con una función de ventana y filtra afuera (funciona en MySQL 8, PostgreSQL, SQL Server y SQLite 3.25 o posteriores):
SELECT id, ciudad, total, prom_ciudad
FROM (SELECT id, ciudad, total,
AVG(total) OVER (PARTITION BY ciudad) AS prom_ciudad
FROM pedidos) t
WHERE total > prom_ciudad;
| id | ciudad | total | prom_ciudad |
|---|---|---|---|
| 6 | Guadalajara | 2400 | 1290.0 |
| 8 | Monterrey | 1600 | 735.0 |
| 2 | Puebla | 1220 | 1000.0 |
La subconsulta es necesaria porque una función de ventana no se puede usar directamente en el WHERE de la misma consulta.
Errores comunes
aggregate functions are not allowed in WHERE(PostgreSQL) oAn aggregate may not appear in the WHERE clause(SQL Server). Moviste elAVGal lugar equivocado: va enHAVING.- Usar el alias en
HAVING. Funciona en MySQL y SQLite, falla en PostgreSQL y SQL Server. Repite la expresión. HAVINGsinGROUP BY. Es válido, pero trata toda la tabla como un solo grupo: devuelve una fila o ninguna.- Comparar con un promedio que incluye
NULLcomo cero.AVGignora losNULL; si tu subconsulta usaSUM(total) / COUNT(*), el promedio baja. Ver AVG y NULL.
La idea es la misma que PROMEDIO.SI en Excel, solo que en SQL decides si la condición se aplica a filas (WHERE) o a grupos (HAVING).