AVG
La función AVG calcula el promedio aritmético de un conjunto de valores numéricos. Es esencial para cualquier análisis de datos: precio medio de productos, valor medio de pedidos, calificación media de empleados, tiempo medio de respuesta. Pero hay un detalle que muchos desarrolladores pasan por alto: AVG ignora los valores NULL, lo que puede producir resultados diferentes a los que esperas si no lo tienes en cuenta.
Sintaxis
AVG(expresion)
AVG(ALL expresion)
AVG(DISTINCT expresion)AVG(expresion) calcula la media de todos los valores no nulos. Es equivalente a dividir SUM(expresion) entre COUNT(expresion). AVG(ALL expresion) es idéntico al anterior ya que ALL es el comportamiento por defecto. AVG(DISTINCT expresion) calcula la media solo de los valores únicos, descartando duplicados antes del cálculo.
Comportamiento básico
Para obtener el precio medio de todos los productos:
SELECT
ROUND(AVG(precio), 2) AS precio_medio,
MIN(precio) AS precio_minimo,
MAX(precio) AS precio_maximo
FROM productos;| precio_medio | precio_minimo | precio_maximo |
|---|---|---|
| 384.52 | 9.99 | 1899.99 |
ROUND con dos decimales es casi obligatorio cuando usas AVG, ya que el resultado suele tener muchos decimales. El precio medio de 384.52 euros te da una referencia central, pero los extremos (9.99 y 1899.99) te dicen que la distribución de precios es muy amplia.
Caso práctico: valor medio de pedido por cliente
El valor medio de pedido (ticket medio) es una métrica fundamental en comercio electrónico:
SELECT
cl.nombre,
cl.apellidos,
COUNT(DISTINCT p.id) AS num_pedidos,
ROUND(AVG(p.total), 2) AS ticket_medio
FROM clientes cl
JOIN pedidos p ON cl.id = p.cliente_id
WHERE p.estado = 'completado'
GROUP BY cl.id, cl.nombre, cl.apellidos
ORDER BY ticket_medio DESC
LIMIT 8;| nombre | apellidos | num_pedidos | ticket_medio |
|---|---|---|---|
| Pedro | Sánchez Gómez | 5 | 1178.04 |
| María | García López | 8 | 1115.06 |
| Carlos | Rodríguez Martín | 7 | 1092.90 |
| Laura | Fernández Díaz | 5 | 1046.12 |
| Ana | Martínez Ruiz | 6 | 1071.79 |
| Jorge | Hernández Polo | 4 | 987.50 |
| Lucía | López Torres | 3 | 952.30 |
| Miguel | Díaz Serrano | 4 | 890.25 |
Observa que María tiene el mayor número de pedidos y un ticket medio alto, lo que la convierte en una cliente muy valiosa. Pedro tiene menos pedidos pero el ticket medio más alto.
Caso práctico: precio medio por categoría
Para comparar los precios medios entre categorías:
SELECT
c.nombre AS categoria,
COUNT(*) AS num_productos,
ROUND(AVG(p.precio), 2) AS precio_medio,
ROUND(AVG(p.precio * p.stock), 2) AS valor_medio_inventario
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
GROUP BY c.nombre
ORDER BY precio_medio DESC;| categoria | num_productos | precio_medio | valor_medio_inventario |
|---|---|---|---|
| Portátiles | 8 | 1245.50 | 37365.00 |
| Smartphones | 12 | 785.42 | 31417.00 |
| Deportes | 4 | 189.99 | 9500.00 |
| Hogar | 5 | 124.80 | 8736.00 |
| Ropa mujer | 6 | 64.99 | 9749.00 |
| Ropa hombre | 6 | 59.99 | 8999.00 |
| Accesorios electrónicos | 7 | 42.85 | 6427.50 |
| Libros | 2 | 22.50 | 1125.00 |
Los portátiles tienen el precio medio más alto, lo cual es esperable. Pero fíjate en la ropa: aunque los precios medios son bajos, el valor medio de inventario es relativamente alto porque se almacenan más unidades.
Caso práctico: AVG ignora NULL y por qué importa
Este es el aspecto más importante de AVG y la fuente de errores más común. Supongamos que tienes una tabla de evaluaciones de empleados donde no todos han sido evaluados:
SELECT
d.nombre AS departamento,
COUNT(*) AS empleados,
COUNT(e.calificacion) AS evaluados,
ROUND(AVG(e.calificacion), 2) AS media_calificacion
FROM empleados e
JOIN departamentos d ON e.departamento_id = d.id
GROUP BY d.nombre;| departamento | empleados | evaluados | media_calificacion |
|---|---|---|---|
| Ventas | 8 | 6 | 8.25 |
| Ingeniería | 10 | 10 | 7.80 |
| Marketing | 5 | 3 | 8.67 |
| Soporte | 7 | 7 | 7.43 |
Marketing tiene la media más alta con 8.67, pero solo 3 de sus 5 empleados fueron evaluados. Si los dos empleados sin evaluar tuvieran calificaciones bajas, la media real sería diferente. Cuando usas AVG, siempre pregúntate: los valores NULL que estoy ignorando, ¿deberían contar como cero?
Si la respuesta es sí, usa IFNULL para convertir los NULL en cero antes de promediar:
SELECT
d.nombre AS departamento,
ROUND(AVG(e.calificacion), 2) AS media_sin_null,
ROUND(AVG(IFNULL(e.calificacion, 0)), 2) AS media_con_ceros
FROM empleados e
JOIN departamentos d ON e.departamento_id = d.id
GROUP BY d.nombre;| departamento | media_sin_null | media_con_ceros |
|---|---|---|
| Ventas | 8.25 | 6.19 |
| Ingeniería | 7.80 | 7.80 |
| Marketing | 8.67 | 5.20 |
| Soporte | 7.43 | 7.43 |
La diferencia es drástica para Marketing: de 8.67 baja a 5.20 cuando los no evaluados cuentan como cero. Ingeniería y Soporte no cambian porque todos sus empleados fueron evaluados.
Manejo de NULL
Resumamos el comportamiento de AVG con NULL:
SELECT
AVG(precio) AS avg_normal,
SUM(precio) / COUNT(*) AS division_total,
SUM(precio) / COUNT(precio) AS division_no_null
FROM productos;| avg_normal | division_total | division_no_null |
|---|---|---|
| 384.52 | 369.14 | 384.52 |
AVG(precio) es equivalente a SUM(precio) / COUNT(precio), no a SUM(precio) / COUNT(*). La diferencia solo importa cuando hay valores NULL. Si precio nunca es NULL, las tres expresiones dan el mismo resultado.
Cuando todas las filas de un grupo tienen NULL, AVG devuelve NULL:
SELECT COALESCE(AVG(descuento), 0) AS descuento_medio
FROM productos
WHERE categoria_id = 99;COALESCE convierte ese NULL en cero cuando no hay datos.
Combinación con otras funciones
AVG se combina bien con funciones de ventana para comparar cada valor con la media de su grupo:
SELECT
c.nombre AS categoria,
ROUND(AVG(p.precio), 2) AS precio_medio_categoria,
ROUND(AVG(AVG(p.precio)) OVER(), 2) AS precio_medio_global
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
GROUP BY c.nombre
ORDER BY precio_medio_categoria DESC;También puedes usar AVG en HAVING para filtrar grupos por su promedio:
SELECT
c.nombre AS categoria,
ROUND(AVG(p.precio), 2) AS precio_medio
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
GROUP BY c.nombre
HAVING AVG(p.precio) > 100
ORDER BY precio_medio DESC;| categoria | precio_medio |
|---|---|
| Portátiles | 1245.50 |
| Smartphones | 785.42 |
| Deportes | 189.99 |
| Hogar | 124.80 |
Esta consulta muestra solo las categorías cuyo precio medio supera los 100 euros.
Para calcular promedios ponderados, puedes usar SUM y COUNT combinados:
SELECT
ROUND(
SUM(p.precio * dp.cantidad) / SUM(dp.cantidad)
, 2) AS precio_medio_ponderado
FROM detalle_pedidos dp
JOIN productos p ON dp.producto_id = p.id;El promedio ponderado tiene en cuenta que los productos más vendidos influyen más en la media, lo que da una imagen más realista del precio típico de venta.
Practica con AVG
Usa el editor para calcular promedios:
En el siguiente artículo veremos MIN para obtener el valor mínimo.
Escrito por Eduardo Lázaro
