Agrupar y resumir
💡 🎯 Al terminar esta lección podrás…
- Usar
COUNT,SUM,AVG,MINyMAX. - Agrupar con
GROUP BYy combinarlo conJOIN. - Filtrar grupos con
HAVINGy distinguirlo deWHERE. - Manejar los
NULLen agregaciones conCOUNT(columna)yCOALESCE.
1.Funciones de agregación
Hasta ahora cada fila del resultado era una fila de la tabla. Las funciones de agregación resumen muchas filas en un solo valor:
| Función | Qué calcula |
|---|---|
COUNT(*) | Cuántas filas hay |
SUM(col) | La suma de los valores |
AVG(col) | El promedio |
MIN(col) / MAX(col) | El menor y el mayor valor |
SELECT COUNT(*) AS cuantos,
SUM(stock) AS stock_total,
AVG(precio) AS promedio,
MIN(precio) AS barato,
MAX(precio) AS caro
FROM productos;
Resultado esperado
| cuantos | stock_total | promedio | barato | caro |
|---|---|---|---|---|
| 8 | 203 | 258.55 | 15.00 | 899.00 |
Sin GROUP BY, toda la tabla (o las filas que pasen el WHERE) se resume en una sola fila. No puedes mezclar una columna normal con una agregada si no la agrupas.
⚠️ Importante · COUNT(*) frente a COUNT(columna)
COUNT(*) cuenta filas; COUNT(columna) cuenta solo las filas donde esa columna no es NULL. Del mismo modo, SUM, AVG, MIN y MAX ignoran los NULL.
2.Cómo tratan los NULL
Al unir clientes con pedidos con LEFT JOIN (lección 5), Diego aparece con NULL en el pedido. Mira la diferencia:
SELECT COUNT(*) AS filas, COUNT(p.id) AS con_pedido
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id;
Resultado esperado
| filas | con_pedido |
|---|---|
| 5 | 4 |
Hay 5 filas (4 pedidos y la fila de Diego), pero solo 4 tienen pedido. Elegir bien entre COUNT(*) y COUNT(columna) es el error más común al contar «cuántos hay».
3.GROUP BY: un resumen por grupo
GROUP BY reúne las filas que comparten el mismo valor en una columna y aplica la función a cada grupo:
SELECT categoria,
COUNT(*) AS cuantos,
AVG(precio) AS promedio,
SUM(stock) AS stock_total
FROM productos
GROUP BY categoria
ORDER BY categoria;
Resultado esperado
| categoria | cuantos | promedio | stock_total |
|---|---|---|---|
| Accesorios | 1 | 15.00 | 100 |
| Audio | 2 | 94.45 | 25 |
| Pantallas | 2 | 774.50 | 11 |
| Periféricos | 3 | 105.17 | 67 |
La regla: toda columna del SELECT debe estar en el GROUP BY o dentro de una función de agregación. Aquí, categoria está agrupada y el resto va dentro de COUNT, AVG y SUM.
📌 Nota · Qué pasa si se incumple
PostgreSQL rechaza una columna no agrupada. SQLite lo permite y elige un valor cualquiera del grupo, lo que da resultados engañosos. Escribe siempre las consultas como si el motor fuera estricto.
4.Agrupar con JOIN: informes reales
Lo potente llega al combinar JOIN y GROUP BY. ¿Cuántos pedidos ha hecho y cuánto ha gastado cada cliente? El gasto de cada línea es cantidad × precio.
SELECT c.nombre,
COUNT(DISTINCT p.id) AS pedidos,
COALESCE(SUM(l.cantidad * pr.precio), 0) AS gasto
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
LEFT JOIN lineas_pedido l ON l.pedido_id = p.id
LEFT JOIN productos pr ON pr.id = l.producto_id
GROUP BY c.id
ORDER BY gasto DESC;
Resultado esperado
| nombre | pedidos | gasto |
|---|---|---|
| Luis Pérez | 1 | 929.00 |
| Marta Ruiz | 1 | 334.50 |
| Ana Torres | 2 | 315.50 |
| Diego Soto | 0 | 0.00 |
LEFT JOINmantiene a Diego en el informe.COUNT(DISTINCT p.id)cuenta pedidos distintos (un pedido con varias líneas aparece varias veces antes de agrupar).COALESCE(valor, 0)sustituyeNULLpor 0: así Diego muestra 0.00 y noNULL.
5.HAVING: filtrar grupos
WHERE filtra filas antes de agrupar. Para filtrar grupos ya calculados (por ejemplo «solo las categorías con 2 o más productos») se usa HAVING:
SELECT categoria, COUNT(*) AS cuantos
FROM productos
GROUP BY categoria
HAVING COUNT(*) >= 2;
Resultado esperado
| categoria | cuantos |
|---|---|
| Audio | 2 |
| Pantallas | 2 |
| Periféricos | 3 |
Y puedes combinar ambos: primero WHERE descarta los productos baratos y después HAVING exige 2 o más por categoría:
SELECT categoria, COUNT(*) AS cuantos
FROM productos
WHERE precio > 50
GROUP BY categoria
HAVING COUNT(*) >= 2;
Resultado esperado
| categoria | cuantos |
|---|---|
| Audio | 2 |
| Pantallas | 2 |
| Periféricos | 2 |
Esto es el orden en que el motor trabaja:
Por eso en WHERE no se puede usar COUNT(*) (los grupos aún no existen) y en HAVING sí.
6.Pruébalo: laboratorio de resúmenes
Elige la función, si quieres agrupar por categoría y si quieres exigir al menos 2 productos por grupo. Cada combinación es una consulta real ejecutada en SQLite sobre productos.
🧪 Ejemplo · Experimentos
1) Sin agrupar, cada función devuelve una sola fila. 2) Activa GROUP BY: ahora una por categoría. 3) Activa HAVING con COUNT(*): desaparece Accesorios, que solo tiene un producto. 4) Mira que HAVING solo se puede activar si hay GROUP BY.
7.Errores frecuentes
- Columna sin agrupar:
SELECT categoria, nombre, COUNT(*) … GROUP BY categoriamezcla una columna que no está agrupada. PostgreSQL da error; SQLite devuelve un valor cualquiera. - Usar
WHERE COUNT(*) > 1: los grupos aún no existen en elWHERE. UsaHAVING. - Confundir
COUNT(*)conCOUNT(columna)cuando hayNULL(por ejemplo tras unLEFT JOIN). - Contar filas duplicadas por un JOIN: si un pedido tiene 3 líneas, aparece 3 veces. Usa
COUNT(DISTINCT p.id). - Olvidar que
AVGignora losNULL: el promedio es solo de los valores que existen. - Filtrar con
HAVINGalgo que podía ir enWHERE: funciona, pero es menos eficiente porque agrupa más filas de las necesarias. - Mostrar decimales largos: redondea con
ROUND(AVG(precio), 2).
📒 Apuntes para llevar
COUNT,SUM,AVG,MINyMAXresumen muchas filas en un valor.COUNT(*)cuenta filas;COUNT(col)cuenta las que no sonNULL. El resto de agregaciones ignoran losNULL.GROUP BY colcalcula la agregación por cada valor distinto decol.- Toda columna del
SELECTdebe estar agrupada o dentro de una función de agregación. WHEREfiltra filas antes de agrupar;HAVINGfiltra grupos después.- Orden de ejecución:
FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. COALESCE(x, 0)sustituyeNULLpor un valor;COUNT(DISTINCT x)cuenta valores distintos.
8.Glosario
| Término | Significado |
|---|---|
| Agregación | Cálculo que resume varias filas en un solo valor. |
| COUNT(*) | Cuenta las filas del grupo o de la tabla. |
| SUM / AVG | Suma y promedio de los valores de una columna, ignorando NULL. |
| MIN / MAX | Menor y mayor valor de una columna. |
| GROUP BY | Cláusula que reúne las filas con el mismo valor para calcular un resumen por grupo. |
| HAVING | Cláusula que filtra grupos después de calcular las agregaciones. |
| COALESCE | Función que devuelve el primer valor que no es NULL. |
| DISTINCT | Palabra que elimina duplicados, también dentro de COUNT(DISTINCT …). |
🧠 Comprueba lo aprendido
Elige una respuesta en cada pregunta. Verás al instante si acertaste y por qué.
¿Qué devuelve SELECT COUNT(*) FROM productos;?
Sin GROUP BY, una agregación resume toda la tabla en una fila.
¿Cuál es la diferencia entre COUNT(*) y COUNT(columna)?
COUNT(*) cuenta todas las filas; COUNT(columna) solo las que tienen valor en esa columna.
¿Qué hace GROUP BY categoria?
Reúne las filas con la misma categoría y aplica las funciones de agregación a cada grupo.
¿Cómo se filtran las categorías que tienen al menos 2 productos?
El resultado de COUNT(*) existe después de agrupar, así que se filtra con HAVING.
¿Cuál es el orden correcto de ejecución?
Primero se obtienen las filas, luego se filtran, se agrupan, se filtran los grupos y al final se eligen las columnas.
Tu progreso se guarda en este navegador y se refleja en el índice del curso.
💡 🚀 ¡Ahora te toca!
La práctica fija lo aprendido. Pasa al ejercicio de esta lección.