lección 4
Contar, sumar y promediar: funciones de agregación
COUNT, SUM, AVG, MIN y MAX — convertir miles de filas en un solo número que responde una pregunta de negocio.
⏱ 55 min
Hasta ahora, tus consultas devuelven filas individuales: cada cliente, cada pedido, cada producto. Pero cuando el director financiero pregunta "¿cuánto vendimos este trimestre?", no quiere ver 14.000 filas de pedidos — quiere UN número. Cuando marketing pregunta "¿cuál es nuestro ticket medio?", espera una respuesta, no una tabla interminable. Las funciones de agregación son exactamente eso: toman N filas y las colapsan en un solo valor que responde una pregunta de negocio.
La analogía perfecta: imagina que tienes un montón de recibos de compra sobre la mesa. "Contar" los recibos es COUNT. "Sumar" todos los importes es SUM. "Calcular la media" es AVG. "Buscar el más alto" es MAX. Son operaciones que hacemos mentalmente con datos pequeños, pero que SQL ejecuta sobre millones de filas en milisegundos.
### Las 5 funciones de agregación fundamentales
- COUNT(*) — cuenta el número TOTAL de filas (incluye filas con NULLs)
- COUNT(columna) — cuenta filas donde esa columna NO es NULL
- COUNT(DISTINCT columna) — cuenta valores ÚNICOS (sin repetir)
- SUM(columna) — suma todos los valores numéricos de la columna
- AVG(columna) — media aritmética (ignora NULLs automáticamente)
- MAX(columna) — el valor más grande (funciona con números, fechas y texto)
- MIN(columna) — el valor más pequeño
1-- ¿Cuántos clientes tenemos en total?2SELECT COUNT(*) AS total_clientes FROM clientes;34-- ¿Cuánto hemos facturado en total?5SELECT ROUND(SUM(importe), 2) AS ventas_totales FROM pedidos;67-- ¿Cuál es el importe medio de un pedido (ticket medio)?8SELECT ROUND(AVG(importe), 2) AS ticket_medio FROM pedidos;910-- ¿Cuál es el pedido más grande y el más pequeño?11SELECT12 MAX(importe) AS pedido_mayor,13 MIN(importe) AS pedido_menor14FROM pedidos;
Cada función toma miles de filas y devuelve UN número
### COUNT(*) vs COUNT(columna) vs COUNT(DISTINCT)
Esta es una distinción que separa a los juniors de los mids. COUNT tiene tres variantes con comportamientos muy diferentes, y confundirlas genera bugs sutiles en reportes financieros:
1-- COUNT(*): cuenta TODAS las filas, incluso las que tienen NULLs2SELECT COUNT(*) AS total_filas FROM clientes;34-- COUNT(ciudad): cuenta filas donde ciudad NO es NULL5-- (habrá menos si hay clientes sin ciudad)6SELECT COUNT(ciudad) AS con_ciudad FROM clientes;78-- COUNT(DISTINCT ciudad): ¿cuántas ciudades diferentes tenemos?9SELECT COUNT(DISTINCT ciudad) AS ciudades_unicas FROM clientes;1011-- La diferencia en acción:12SELECT13 COUNT(*) AS filas_totales,14 COUNT(ciudad) AS con_ciudad,15 COUNT(DISTINCT ciudad) AS ciudades_distintas16FROM clientes;
Tres COUNTs, tres significados — elige el correcto
Consejo de senior: en mi segundo trabajo, un reporte financiero daba 2% menos de ventas que el ERP. Después de 3 horas de debug, descubrí que alguien usaba COUNT(importe) en vez de COUNT(*), y había pedidos con importe NULL (pagos pendientes). COUNT(columna) ignora NULLs silenciosamente. Siempre verifica cuál estás usando.
### Combinar múltiples agregaciones en una sola query
No necesitas ejecutar 5 queries separadas para obtener 5 métricas. SQL permite combinar todas las funciones de agregación en un solo SELECT. Este patrón es la base de cualquier dashboard:
1-- Dashboard de métricas en UNA sola query2SELECT3 COUNT(*) AS total_pedidos,4 COUNT(DISTINCT cliente_id) AS clientes_activos,5 ROUND(SUM(importe), 2) AS facturacion_total,6 ROUND(AVG(importe), 2) AS ticket_medio,7 MAX(importe) AS pedido_max,8 MIN(importe) AS pedido_min,9 MAX(fecha) AS ultimo_pedido10FROM pedidos11WHERE estado = 'completado';
Un dashboard completo en una sola consulta
### SUM y AVG: cuidado con los tipos
SUM sube de tipo solo: si sumas una columna de enteros te devuelve un entero grande, así que no se desborda por mucho que sumes. Lo que sí desborda es forzar el resultado de vuelta a un entero normal con ::INTEGER — y eso no da un número raro, da un error y la consulta no corre. Así que no lo hagas. AVG, en cambio, devuelve un decimal con muchas cifras. Para eso está ROUND: ROUND(AVG(importe), 2) te lo deja en dos decimales. Y ojo, en dinero se redondea para MOSTRAR, nunca para calcular — redondea al final, en la última línea, no en medio.
1-- SUM devuelve un número grande — usa ROUND para dejarlo legible2SELECT ROUND(SUM(importe), 2) AS total FROM pedidos;34-- AVG da muchos decimales — usa ROUND5SELECT ROUND(AVG(importe), 2) AS media FROM pedidos;67-- Combina ambos para reportes limpios8SELECT9 ROUND(SUM(importe), 2) AS total_ventas,10 ROUND(AVG(importe), 2) AS ticket_medio,11 ROUND(SUM(importe) / COUNT(DISTINCT cliente_id), 2) AS gasto_medio_por_cliente12FROM pedidos13WHERE estado = 'completado';
ROUND para resultados legibles en reportes
### MIN y MAX: no solo para números
MAX y MIN funcionan con cualquier tipo que se pueda ordenar: números, fechas, e incluso texto (orden alfabético). Esto los hace extremadamente útiles para encontrar rangos temporales:
1-- Rango de fechas de los pedidos2SELECT3 MIN(fecha) AS primer_pedido,4 MAX(fecha) AS ultimo_pedido,5 MAX(fecha) - MIN(fecha) AS dias_de_actividad6FROM pedidos;78-- Rango de precios por categoría9SELECT10 MIN(precio) AS precio_minimo,11 MAX(precio) AS precio_maximo,12 MAX(precio) - MIN(precio) AS rango_precios13FROM productos;
MIN/MAX con fechas: descubrir el rango temporal de tus datos
Las funciones de agregación ignoran NULLs (excepto COUNT(*)). Si tienes 100 pedidos y 5 tienen importe NULL, AVG(importe) calcula la media de los 95 que SÍ tienen valor. Esto puede sesgar tus resultados sin que te des cuenta.
### Agregación con filtro: WHERE antes de agregar
Puedes combinar WHERE con funciones de agregación para calcular métricas sobre un subconjunto de datos. El WHERE se ejecuta PRIMERO (filtra filas) y LUEGO la agregación actúa sobre las filas que pasaron el filtro:
1-- Solo pedidos completados de 20242SELECT3 COUNT(*) AS pedidos_2024,4 ROUND(SUM(importe), 2) AS total_2024,5 ROUND(AVG(importe), 2) AS ticket_medio_20246FROM pedidos7WHERE estado = 'completado'8 AND fecha >= '2024-01-01';
WHERE filtra ANTES, la agregación actúa DESPUÉS
UNION ALL: a veces necesitas apilar los resultados de varias queries en una sola tabla. UNION ALL combina dos SELECTs verticalmente (requisito: mismo número de columnas y tipos compatibles). Ejemplo: un SELECT para completados + UNION ALL + un SELECT para cancelados → una tabla con ambos. UNION ALL mantiene duplicados (más rápido); UNION sin ALL los elimina. Lo usarás en los ejercicios siguientes.
Regístrate para guardar tu progreso.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...