Promedio por grupo en SQL con GROUP BY

Por el equipo de promedio.net · Actualizado el

Para obtener un promedio por categoría en SQL, combina AVG con GROUP BY: SELECT materia, AVG(calificacion) FROM calificaciones GROUP BY materia. El motor separa las filas en grupos según la columna indicada y devuelve una fila por grupo con su promedio. Toda columna del SELECT que no esté dentro de una función de agregado debe aparecer en el GROUP BY.

Sintaxis

SELECT columna_grupo, AVG(columna_valor) AS promedio
FROM tabla
[WHERE condicion_de_filas]
GROUP BY columna_grupo
[HAVING condicion_de_grupos]
[ORDER BY promedio DESC];

El orden de ejecución explica casi todos los errores: primero FROM, luego WHERE (filtra filas), después GROUP BY (forma grupos), HAVING (filtra grupos), SELECT (calcula) y al final ORDER BY.

Paso a paso

  1. Decide qué define un grupo: materia, sucursal, mes, cliente.
  2. Escribe esa columna en el SELECT y en el GROUP BY.
  3. Agrega AVG(columna) con un alias claro.
  4. Suma COUNT(*) para saber cuántas filas respaldan cada promedio.
  5. Ordena con ORDER BY usando el alias.

Tabla de ejemplo:

CREATE TABLE calificaciones (
  alumno       VARCHAR(30),
  grupo        CHAR(2),
  materia      VARCHAR(20),
  calificacion DECIMAL(4, 1)
);
INSERT INTO calificaciones VALUES
  ('Andrea', '3A', 'Matemáticas', 9.0),
  ('Andrea', '3A', 'Historia',    8.5),
  ('Bruno',  '3A', 'Matemáticas', 6.0),
  ('Bruno',  '3A', 'Historia',    7.5),
  ('Celia',  '3B', 'Matemáticas', 8.0),
  ('Celia',  '3B', 'Historia',    9.5),
  ('Diego',  '3B', 'Matemáticas', 7.0),
  ('Diego',  '3B', 'Historia',    6.0),
  ('Elena',  '3B', 'Matemáticas', 10.0);

Ejemplos resueltos

Ejemplo 1: promedio por materia

SELECT materia,
       ROUND(AVG(calificacion), 2) AS promedio,
       COUNT(*)                    AS alumnos
FROM calificaciones
GROUP BY materia
ORDER BY promedio DESC;
materiapromedioalumnos
Matemáticas8.05
Historia7.884

Historia promedia 31.5 / 4 = 7.875, que ROUND lleva a 7.88. En SQL Server verías 8.000000 y 7.880000: el promedio de un DECIMAL conserva al menos seis decimales y ROUND no los quita. Para mostrar solo dos, convierte con CAST(AVG(calificacion) AS DECIMAL(4, 2)).

Ejemplo 2: dos columnas de agrupación

SELECT grupo, materia, ROUND(AVG(calificacion), 2) AS promedio
FROM calificaciones
GROUP BY grupo, materia
ORDER BY grupo, materia;
grupomateriapromedio
3AHistoria8.0
3AMatemáticas7.5
3BHistoria7.75
3BMatemáticas8.33

Cada combinación distinta de grupo y materia forma un grupo. Si quieres verlo como tabla cruzada (grupos en filas, materias en columnas), usa CASE dentro de AVG:

SELECT grupo,
  ROUND(AVG(CASE WHEN materia = 'Matemáticas' THEN calificacion END), 2) AS matematicas,
  ROUND(AVG(CASE WHEN materia = 'Historia'    THEN calificacion END), 2) AS historia
FROM calificaciones
GROUP BY grupo;
grupomatematicashistoria
3A7.58.0
3B8.337.75

El CASE sin ELSE devuelve NULL para las otras materias, y AVG ignora esos NULL. Con ELSE 0 el promedio saldría mal.

Ejemplo 3: promedio de filas frente a promedio de alumnos

En el grupo 3B, Elena solo tiene una calificación. Compara dos formas de calcular el promedio del grupo:

-- promedio de todas las calificaciones del grupo
SELECT grupo, ROUND(AVG(calificacion), 2) AS prom_filas
FROM calificaciones
GROUP BY grupo;

-- primero el promedio de cada alumno, luego el del grupo
SELECT grupo, ROUND(AVG(prom_alumno), 2) AS prom_de_alumnos
FROM (SELECT grupo, alumno, AVG(calificacion) AS prom_alumno
      FROM calificaciones
      GROUP BY grupo, alumno) t
GROUP BY grupo;
grupoprom_filasprom_de_alumnos
3A7.757.75
3B8.18.42

En 3A coinciden porque cada alumno tiene dos calificaciones. En 3B no: el primer cálculo pesa a cada alumno según cuántas calificaciones tiene; el segundo da el mismo peso a cada alumno, y el 10 de Elena cuenta como un promedio completo. Ninguno está mal, pero responden preguntas distintas.

Errores comunes

El mismo cálculo en otras herramientas: groupby en Python con pandas y PROMEDIO.SI en Excel.