Las funciones de agregado resumen un conjunto de filas en un único valor. Son la base de cualquier informe o estadística: contar registros, calcular totales, promedios, valores máximos y mínimos. En SQL, las funciones de agregado operan sobre grupos de filas definidos por GROUP BY, o sobre la tabla completa si no hay GROUP BY.
En este capítulo cubrimos COUNT, SUM, AVG, MIN, MAX y cómo se comportan con valores NULL, que es la fuente de error más habitual al trabajar con agregados.
COUNT: contar filas
-- COUNT(*): cuenta todas las filas, incluidas las que tienen NULL
SELECT COUNT(*) AS total_clientes FROM clientes;
-- COUNT(columna): cuenta las filas donde esa columna NO es NULL
SELECT COUNT(ciudad) AS clientes_con_ciudad FROM clientes;
-- Si hay clientes sin ciudad (NULL), este número es menor que COUNT(*)
-- COUNT(DISTINCT columna): cuenta valores distintos no-NULL
SELECT COUNT(DISTINCT ciudad) AS ciudades_distintas FROM clientes;
-- Combinar en una sola consulta
SELECT
COUNT(*) AS total,
COUNT(ciudad) AS con_ciudad,
COUNT(*) - COUNT(ciudad) AS sin_ciudad,
COUNT(DISTINCT ciudad) AS ciudades_unicas
FROM clientes;
SUM: suma de valores
-- Total de stock de todos los productos
SELECT SUM(stock) AS stock_total FROM productos;
-- SUM ignora NULL (si un valor es NULL, no suma nada, no suma 0)
-- Si todos los valores son NULL, SUM devuelve NULL
-- Total facturado
SELECT SUM(total) AS facturado_total FROM pedidos WHERE estado = 'pagado';
-- Valor del inventario por categoría
SELECT
c.nombre AS categoria,
SUM(p.precio * p.stock) AS valor_inventario
FROM productos p
JOIN categorias c ON c.id = p.id_categoria
GROUP BY c.nombre
ORDER BY valor_inventario DESC;
AVG: promedio
-- Precio medio de los productos
SELECT ROUND(AVG(precio), 2) AS precio_medio FROM productos;
-- AVG también ignora NULL
-- CUIDADO: AVG(columna) != SUM(columna)/COUNT(*) si hay NULLs
-- AVG divide por el número de valores no-NULL; COUNT(*) cuenta todas las filas
-- Comparar precio de cada producto con el precio medio
SELECT
nombre,
precio,
ROUND(AVG(precio) OVER (), 2) AS precio_medio_global,
precio - ROUND(AVG(precio) OVER (), 2) AS diferencia
FROM productos;
MIN y MAX
-- Precio más bajo y más alto SELECT MIN(precio) AS mas_barato, MAX(precio) AS mas_caro FROM productos; -- Funciona con fechas: primer y último registro SELECT MIN(fecha_alta) AS primer_cliente, MAX(fecha_alta) AS ultimo_cliente FROM clientes; -- Y con cadenas: orden alfabético SELECT MIN(nombre) AS primero_abc, MAX(nombre) AS ultimo_abc FROM clientes; -- Producto más caro (no solo el precio, sino toda la fila) -- Subconsulta necesaria — MIN/MAX solos no devuelven la fila completa: SELECT * FROM productos WHERE precio = (SELECT MAX(precio) FROM productos);
Valores NULL en agregados
-- Demostración del comportamiento con NULL -- Imagina que algunos pedidos tienen total = NULL -- COUNT(*) cuenta la fila aunque total sea NULL SELECT COUNT(*) FROM pedidos; -- SUM, AVG, MIN, MAX ignoran las filas donde la columna es NULL SELECT SUM(total), AVG(total) FROM pedidos; -- Para tratar NULL como cero en SUM: SELECT SUM(COALESCE(total, 0)) FROM pedidos;
GROUP_CONCAT: concatenar valores de un grupo (MySQL)
-- Listar los nombres de productos de cada categoría en una sola celda
SELECT
c.nombre AS categoria,
GROUP_CONCAT(p.nombre ORDER BY p.nombre SEPARATOR ', ') AS productos
FROM categorias c
JOIN productos p ON p.id_categoria = c.id
GROUP BY c.nombre;
