HAVING con AVG y filas por encima del promedio en SQL

Por el equipo de promedio.net · Actualizado el

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

WHEREHAVING
Se aplica aCada filaCada grupo
MomentoAntes de GROUP BYDespués de GROUP BY
Puede usar AVG, SUM, COUNTNoSí
Cambia el promedio calculadoSí: quita filas del cálculoNo: solo oculta grupos

Paso a paso

  1. Escribe la consulta con GROUP BY y AVG y comprueba que los promedios sean correctos.
  2. Si quieres excluir filas del cálculo (pedidos cancelados, montos de prueba), ponlo en WHERE.
  3. Si quieres ocultar grupos según su promedio o su tamaño, agrega HAVING con la expresión completa, AVG(total).
  4. 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;
clienteticket_promediopedidos
Café Norte1600.01
Papelería Río1290.02

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;
ciudadsin WHEREcon WHERE total >= 300
Guadalajara1290.02400.0
Monterrey735.0955.0
Puebla1000.01000.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;
idclientetotal
6Papelería Río2400
8Café Norte1600
2Ferretería Luna1220

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;
idciudadtotalprom_ciudad
6Guadalajara24001290.0
8Monterrey1600735.0
2Puebla12201000.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

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).