GROUP BY y HAVING en SQL: COUNT, SUM y AVG con ejemplos
Actualizado el 29 de septiembre de 2026
Hasta ahora cada fila del resultado correspondía a una fila de la tabla. Las funciones de agregación hacen otra cosa: resumen muchas filas en un solo valor, como un total, un promedio o una cantidad. Con GROUP BY ese resumen se calcula por grupo: el stock total por categoría, el promedio de ventas por mes.
Todos los ejemplos usan esta tabla de productos de una tienda. Fíjate en que el escritorio no tiene stock registrado (NULL), que no es lo mismo que tener 0.
CREATE TABLE productos (
id INTEGER PRIMARY KEY,
nombre TEXT,
categoria TEXT,
precio INTEGER,
stock INTEGER
);| id | nombre | categoria | precio | stock |
|---|---|---|---|---|
| 1 | Teclado | Computación | 25990 | 12 |
| 2 | Mouse | Computación | 12990 | 30 |
| 3 | Monitor | Computación | 159990 | 4 |
| 4 | Audífonos | Audio | 34990 | 0 |
| 5 | Parlante | Audio | 49990 | 7 |
| 6 | Silla | Muebles | 89990 | 3 |
| 7 | Escritorio | Muebles | 129990 | NULL |
Funciones de agregación
SELECT COUNT(*) AS productos,
SUM(stock) AS stock_total,
AVG(precio) AS precio_promedio,
MIN(precio) AS mas_barato,
MAX(precio) AS mas_caro
FROM productos;| productos | stock_total | precio_promedio | mas_barato | mas_caro |
|---|---|---|---|---|
| 7 | 56 | 71990.0 | 12990 | 159990 |
Sin GROUP BY, toda la tabla es un solo grupo y el resultado tiene una fila. Las funciones de agregación ignoran los NULL: SUM(stock) suma los seis stocks conocidos. COUNT(*) cuenta filas, pero COUNT(stock) cuenta valores no nulos: da 6, no 7.
GROUP BY: un resumen por grupo
SELECT categoria, COUNT(*) AS cantidad
FROM productos
GROUP BY categoria;| categoria | cantidad |
|---|---|
| Audio | 2 |
| Computación | 3 |
| Muebles | 2 |
GROUP BY categoria junta las filas que tienen la misma categoría y calcula la agregación para cada grupo. La regla clave: en el SELECT solo pueden ir las columnas del GROUP BY y funciones de agregación. Pedir el nombre no tendría sentido: cada grupo tiene varios nombres.
SELECT categoria, ROUND(AVG(precio)) AS promedio
FROM productos
GROUP BY categoria;| categoria | promedio |
|---|---|
| Audio | 42490.0 |
| Computación | 66323.0 |
| Muebles | 109990.0 |
El promedio exacto de Computación es 66323.333…; ROUND lo redondea, aunque en SQLite el resultado sigue siendo decimal (66323.0).
HAVING: filtrar grupos
WHERE filtra filas antes de agrupar; HAVING filtra grupos después, y por eso puede usar funciones de agregación:
SELECT categoria, COUNT(*) AS cantidad
FROM productos
GROUP BY categoria
HAVING COUNT(*) >= 3;| categoria | cantidad |
|---|---|
| Computación | 3 |
Una consulta puede tener ambos. El orden de ejecución ayuda a entenderlo: FROM → WHERE (filas) → GROUP BY → HAVING (grupos) → SELECT → ORDER BY → LIMIT.
Errores comunes
Usar una agregación en el WHERE
SELECT categoria FROM productos
WHERE COUNT(*) > 1
GROUP BY categoria;
-- Error: misuse of aggregate: COUNT()Cuando se evalúa el WHERE todavía no existen los grupos, así que no hay nada que contar. Las condiciones sobre agregaciones van en HAVING.
Columnas que no están en el GROUP BY
SELECT categoria, nombre FROM productos GROUP BY categoria falla en PostgreSQL ("must appear in the GROUP BY clause or be used in an aggregate function"). SQLite, en cambio, la ejecuta y devuelve un nombre cualquiera de cada grupo, lo que es peor: el resultado parece correcto y no lo es. Si necesitas "el producto más caro de cada categoría", la herramienta es una subconsulta (ver la guía siguiente).
COUNT(*) en vez de COUNT(columna)
En Muebles, COUNT(*) es 2 y COUNT(stock) es 1, porque el escritorio no tiene stock registrado. Pregúntate qué quieres contar: filas (COUNT(*)) o valores conocidos de una columna (COUNT(columna)).
Ejercicios resueltos
Intenta resolver cada ejercicio antes de abrir la solución: equivocarse y corregir es la parte que más enseña.
1.Productos por categoría
Escribe una consulta que muestre cada categoría y cuántos productos tiene, de la que tiene más a la que tiene menos. Si dos categorías empatan, ordénalas alfabéticamente.
Ver soluciónOcultar solución
SELECT categoria, COUNT(*) AS cantidad
FROM productos
GROUP BY categoria
ORDER BY cantidad DESC, categoria;| categoria | cantidad |
|---|---|
| Computación | 3 |
| Audio | 2 |
| Muebles | 2 |
Se agrupa por categoría y se cuenta cada grupo. El ORDER BY puede usar el alias cantidad porque se evalúa después del SELECT. Audio y Muebles empatan con 2: sin la segunda columna del ORDER BY, la base de datos puede devolverlas en cualquier orden (SQLite, de hecho, pone Muebles primero).
2.Categorías caras
Escribe una consulta que muestre las categorías cuyo precio promedio supera los 50.000, junto con ese promedio redondeado.
Ver soluciónOcultar solución
SELECT categoria, ROUND(AVG(precio)) AS promedio
FROM productos
GROUP BY categoria
HAVING AVG(precio) > 50000;| categoria | promedio |
|---|---|
| Computación | 66323.0 |
| Muebles | 109990.0 |
La condición es sobre el promedio de cada grupo, así que va en HAVING. Audio queda fuera con un promedio de 42.490. Computación entra aunque dos de sus tres productos son baratos: el monitor sube el promedio, un buen recordatorio de que el promedio es sensible a los valores extremos.
3.Stock por categoría, sin los productos más caros
Escribe una consulta que muestre el stock total de cada categoría considerando solo los productos de menos de 100.000, ordenado de mayor a menor stock.
Ver soluciónOcultar solución
SELECT categoria, SUM(stock) AS stock_total
FROM productos
WHERE precio < 100000
GROUP BY categoria
ORDER BY stock_total DESC;| categoria | stock_total |
|---|---|
| Computación | 42 |
| Audio | 7 |
| Muebles | 3 |
Aquí el filtro es sobre productos individuales, no sobre grupos, así que va en WHERE, antes de agrupar. Se descartan el monitor y el escritorio, y después se suma el stock de lo que queda: Computación suma 12 + 30, Audio 0 + 7 y Muebles solo la silla.