Funciones de Agregación en MySQL

Aprendé a resumir y analizar grandes volúmenes de datos con COUNT, SUM, AVG, GROUP BY y más. Son las herramientas clave para generar reportes y estadísticas.

¿Qué son las funciones de agregación?

Las funciones de agregación toman un conjunto de filas y devuelven un único valor que resume ese conjunto. A diferencia de un SELECT normal que devuelve una fila por registro, una función de agregación devuelve una fila con el resumen de todos.

-- Sin agregación: devuelve una fila por producto
SELECT nombre, precio FROM productos;

-- Con agregación: devuelve UN SOLO valor resumen
SELECT COUNT(*) FROM productos;      -- cuántos hay
SELECT AVG(precio) FROM productos;   -- precio promedio

COUNT()

COUNT(*)

Cuenta la cantidad de filas

SUM()

SUM(precio)

Suma los valores de una columna

AVG()

AVG(precio)

Calcula el promedio

MIN() / MAX()

MIN(precio)

El valor mínimo o máximo

Probá el Ejemplo 1: Funciones básicas

COUNT — contar registros

COUNT es la función más usada. Tiene variantes importantes:

-- COUNT(*) — cuenta TODAS las filas, incluyendo NULLs
SELECT COUNT(*) AS total_clientes FROM clientes;

-- COUNT(columna) — cuenta solo las filas donde esa columna NO es NULL
SELECT COUNT(email) AS con_email FROM clientes;

-- COUNT(DISTINCT columna) — cuenta valores únicos
SELECT COUNT(DISTINCT ciudad) AS ciudades_distintas FROM clientes;

-- Contar con condición
SELECT COUNT(*) AS pedidos_entregados
FROM ordenes
WHERE estado = 'entregado';
COUNT(*) vs COUNT(columna)

COUNT(*) cuenta todas las filas sin importar valores NULL. COUNT(columna) ignora las filas donde esa columna es NULL. Elegí la correcta según lo que necesitás contar.

SUM, AVG, MIN y MAX

-- Total de ventas
SELECT SUM(total) AS ventas_totales
FROM ordenes;

-- Ticket promedio por orden
SELECT ROUND(AVG(total), 2) AS ticket_promedio
FROM ordenes;

-- Producto más caro y más barato
SELECT
    MAX(precio) AS precio_maximo,
    MIN(precio) AS precio_minimo,
    MAX(precio) - MIN(precio) AS diferencia
FROM productos;

-- Valor total del inventario (precio × stock)
SELECT ROUND(SUM(precio * stock), 2) AS valor_inventario
FROM productos;
Probá el Ejemplo 2: SUM, AVG, MIN, MAX

GROUP BY — agrupar resultados

GROUP BY divide las filas en grupos según los valores de una columna, y aplica la función de agregación a cada grupo por separado. Es la clave para generar reportes del tipo "totales por categoría", "ventas por mes", etc.

-- Sin GROUP BY: UN total para todos los productos
SELECT COUNT(*) AS total FROM productos;    -- 29

-- Con GROUP BY: UN total POR CADA categoría
SELECT
    id_categoria,
    COUNT(*) AS cantidad_productos
FROM productos
GROUP BY id_categoria;

GROUP BY con JOIN para nombres descriptivos

-- En vez de mostrar el id_categoria, mostramos el nombre
SELECT
    c.nombre              AS categoria,
    COUNT(p.id)           AS cantidad_productos,
    ROUND(AVG(p.precio), 2) AS precio_promedio,
    MIN(p.precio)         AS mas_barato,
    MAX(p.precio)         AS mas_caro
FROM productos p
JOIN categorias c ON p.id_categoria = c.id
GROUP BY c.id, c.nombre
ORDER BY cantidad_productos DESC;
Regla de GROUP BY

Toda columna del SELECT que no sea una función de agregación debe aparecer en el GROUP BY. Si no, MySQL no sabe qué valor mostrar.

Probá el Ejemplo 3: GROUP BY

HAVING — filtrar grupos

WHERE filtra filas antes de agrupar. HAVING filtra grupos después de aplicar el GROUP BY. Se usa cuando la condición involucra una función de agregación.

-- WHERE filtra filas individuales (antes de agrupar)
SELECT ciudad, COUNT(*) AS total
FROM clientes
WHERE pais = 'Argentina'    -- primero filtra por país
GROUP BY ciudad;

-- HAVING filtra grupos (después de agrupar)
SELECT ciudad, COUNT(*) AS total
FROM clientes
GROUP BY ciudad
HAVING total >= 2;          -- solo ciudades con 2 o más clientes

-- Combinar ambos
SELECT
    c.nombre      AS categoria,
    COUNT(p.id)   AS cantidad,
    AVG(p.precio) AS precio_prom
FROM productos p
JOIN categorias c ON p.id_categoria = c.id
WHERE p.precio > 20          -- filtra productos baratos
GROUP BY c.id, c.nombre
HAVING cantidad >= 3         -- solo categorías con 3+ productos
ORDER BY precio_prom DESC;
WHEREHAVING
Cuándo filtraAntes de GROUP BYDespués de GROUP BY
Qué filtraFilas individualesGrupos completos
Puede usarColumnas de la tablaFunciones de agregación
EjemploWHERE precio > 50HAVING COUNT(*) > 3
Probá el Ejemplo 4: HAVING

Orden de ejecución de una query

SQL ejecuta las cláusulas en un orden específico que es importante entender para saber dónde podés usar cada cosa:

SELECT   ciudad, COUNT(*) AS total   -- 6. qué mostrar
FROM     clientes                    -- 1. de qué tabla
WHERE    pais = 'Argentina'          -- 2. filtrar filas
GROUP BY ciudad                      -- 3. agrupar
HAVING   total >= 2                  -- 4. filtrar grupos
ORDER BY total DESC                  -- 5. ordenar
LIMIT    5;                          -- 7. limitar filas
Por eso no podés usar alias en WHERE

El alias total se define en el SELECT (paso 6), pero WHERE se ejecuta en el paso 2 — antes de que el alias exista. En cambio, en HAVING (paso 4) sí podés usarlo porque el SELECT ya se procesó.

Buenas Prácticas

  • Usá ROUND() con AVG para no mostrar 15 decimales: ROUND(AVG(precio), 2)
  • Siempre usá alias en funciones de agregación para que el resultado sea legible
  • GROUP BY todas las columnas no agregadas del SELECT — MySQL en modo estricto lo exige
  • WHERE antes de GROUP BY para reducir filas antes de agrupar — es más eficiente
  • HAVING solo para condiciones sobre agregaciones — si podés usar WHERE, usá WHERE