SQL no tiene una función de promedio ponderado, pero se escribe con dos sumas: SUM(valor * peso) / SUM(peso). Multiplicas cada valor por su peso, sumas esos productos y divides entre la suma de los pesos. Con GROUP BY obtienes un promedio ponderado por grupo, y con NULLIF(SUM(peso), 0) evitas el error de división entre cero.
x̄w = SUM(valor · peso)SUM(peso)
Sintaxis
SELECT grupo,
SUM(valor * peso) / NULLIF(SUM(peso), 0) AS promedio_ponderado
FROM tabla
GROUP BY grupo;
Funciona igual en MySQL, PostgreSQL, SQL Server, Oracle y SQLite. Los pesos pueden ser créditos, unidades, horas o porcentajes; no necesitan sumar 1 ni 100, porque la división entre SUM(peso) los normaliza.
Paso a paso
- Identifica la columna del valor (calificación, precio) y la del peso (créditos, cantidad).
- Escribe
SUM(valor * peso)en el numerador: la multiplicación ocurre fila por fila, antes de sumar. - Escribe
SUM(peso)en el denominador, envuelto enNULLIF(..., 0). - Agrupa con
GROUP BYsi quieres un resultado por alumno, producto o periodo. - Comprueba los tipos: si valor y peso son enteros, multiplica por
1.0para no truncar.
Ejemplos resueltos
Ejemplo 1: promedio por créditos de cada alumno
CREATE TABLE kardex (
alumno VARCHAR(30),
materia VARCHAR(30),
calificacion DECIMAL(4, 1),
creditos INT
);
INSERT INTO kardex VALUES
('Lucía', 'Cálculo', 9.0, 8),
('Lucía', 'Programación', 7.5, 6),
('Lucía', 'Inglés', 8.0, 4),
('Lucía', 'Ética', 6.0, 2),
('Mario', 'Cálculo', 7.0, 8),
('Mario', 'Programación', 9.5, 6),
('Mario', 'Inglés', 10.0, 4);
SELECT alumno,
ROUND(SUM(calificacion * creditos) / SUM(creditos), 2) AS promedio_ponderado,
ROUND(AVG(calificacion), 2) AS promedio_simple,
SUM(creditos) AS creditos
FROM kardex
GROUP BY alumno;
| alumno | promedio_ponderado | promedio_simple | creditos |
|---|---|---|---|
| Lucía | 8.05 | 7.63 | 20 |
| Mario | 8.5 | 8.83 | 18 |
Para Lucía: 9.0 × 8 + 7.5 × 6 + 8.0 × 4 + 6.0 × 2 = 161, y 161 / 20 = 8.05. Su promedio sube con la ponderación porque su mejor nota está en la materia de más créditos. A Mario le pasa lo contrario: su nota más baja, 7.0, está en Cálculo, que vale 8 créditos, y el ponderado (8.5) queda por debajo del simple (8.83).
Ejemplo 2: precio promedio de compra por producto
CREATE TABLE compras (
producto VARCHAR(20),
precio DECIMAL(10, 2),
cantidad INT
);
INSERT INTO compras VALUES
('Café', 180.00, 10), ('Café', 210.00, 30), ('Café', 195.00, 20),
('Azúcar', 32.50, 100), ('Azúcar', 35.00, 0);
SELECT producto,
ROUND(SUM(precio * cantidad) / NULLIF(SUM(cantidad), 0), 2) AS precio_promedio,
ROUND(AVG(precio), 2) AS promedio_simple,
SUM(cantidad) AS unidades
FROM compras
GROUP BY producto;
| producto | precio_promedio | promedio_simple | unidades |
|---|---|---|---|
| Azúcar | 32.5 | 33.75 | 100 |
| Café | 200.0 | 195.0 | 60 |
El café costó 12,000 en total por 60 kilos: 200 por kilo. El promedio simple de los tres precios (195) ignora que la compra más cara fue también la más grande. En el azúcar, la cotización de 35.00 con cantidad 0 no afecta al ponderado, pero sí al simple. Para este cálculo con inventarios existe la calculadora de precio promedio de compra.
Ejemplo 3: un grupo sin pesos
Si filtras solo la fila del azúcar con cantidad 0, la suma de pesos es 0:
SELECT SUM(precio * cantidad) / NULLIF(SUM(cantidad), 0) AS precio_promedio
FROM compras
WHERE cantidad = 0;
El resultado es NULL. Sin NULLIF, SQL Server y PostgreSQL lanzan un error de división entre cero, MySQL devuelve NULL con una advertencia y SQLite devuelve NULL. Con NULLIF el comportamiento es el mismo en todos: NULL indica que no hay datos para promediar.
Errores comunes
SUM(valor) * SUM(peso) / SUM(peso). Multiplicar las sumas no es lo mismo que sumar los productos. El producto va dentro delSUM.- Dividir entre
COUNT(*). Eso da el promedio de los productos, no el promedio ponderado. El divisor esSUM(peso). - División entera. Si
calificacionycreditossonINT, SQL Server, PostgreSQL y SQLite truncan: escribeSUM(calificacion * creditos) * 1.0 / SUM(creditos). Ver AVG con enteros. NULLen el valor pero no en el peso. Esa fila suma su peso al denominador y nada al numerador, y baja el promedio. Filtra conWHERE valor IS NOT NULLo usaSUM(CASE WHEN valor IS NOT NULL THEN peso END)como denominador.- Pesos negativos. Pueden producir promedios fuera del rango de los datos. Revisa devoluciones o ajustes antes de usarlos como peso.
Comprueba el resultado con la calculadora de promedio ponderado. En hoja de cálculo, el equivalente es SUMAPRODUCTO, explicado en el promedio ponderado en Excel.