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_medioprecio_minimoprecio_maximo
384.529.991899.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;
nombreapellidosnum_pedidosticket_medio
PedroSánchez Gómez51178.04
MaríaGarcía López81115.06
CarlosRodríguez Martín71092.90
LauraFernández Díaz51046.12
AnaMartínez Ruiz61071.79
JorgeHernández Polo4987.50
LucíaLópez Torres3952.30
MiguelDíaz Serrano4890.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;
categorianum_productosprecio_mediovalor_medio_inventario
Portátiles81245.5037365.00
Smartphones12785.4231417.00
Deportes4189.999500.00
Hogar5124.808736.00
Ropa mujer664.999749.00
Ropa hombre659.998999.00
Accesorios electrónicos742.856427.50
Libros222.501125.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;
departamentoempleadosevaluadosmedia_calificacion
Ventas868.25
Ingeniería10107.80
Marketing538.67
Soporte777.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;
departamentomedia_sin_nullmedia_con_ceros
Ventas8.256.19
Ingeniería7.807.80
Marketing8.675.20
Soporte7.437.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_normaldivision_totaldivision_no_null
384.52369.14384.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;
categoriaprecio_medio
Portátiles1245.50
Smartphones785.42
Deportes189.99
Hogar124.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:

Simulador SQL
Ctrl+Enter para ejecutar

En el siguiente artículo veremos MIN para obtener el valor mínimo.

Escrito por Eduardo Lázaro