Aprende SQL · lección gratuita
GROUP BY reparte las filas en grupos según el valor de una o varias columnas, y luego aplica las funciones de agregación por grupo en vez de a toda la tabla. Es lo que convierte "el revenue total" en "el revenue de cada vendedor", o "cuántos pedidos hay" en "cuántos pedidos hay por estado". El resultado tiene una fila por grupo distinto.
GROUP BY canal produce una fila por cada canal distinto (Directo, Web, Partner).SELECT, cada columna que no sea un agregado debe aparecer en el GROUP BY. Mostrar una columna no agrupada ni agregada da resultados arbitrarios (SQLite lo permite pero el valor es impredecible).SUM, COUNT, AVG, MIN, MAX se recalculan dentro de cada grupo.d.nombre de departamentos) tras unir.ORDER BY (a menudo sobre el agregado) para presentarlo.SELECT col_grupo, AGG(expr)
FROM tabla
[JOIN ...]
[WHERE filtro_de_filas]
GROUP BY col_grupo
[ORDER BY ...];
El WHERE filtra filas individuales antes de agrupar (no puede contener agregados). Es habitual agrupar por una clave (e.id) pero mostrar un nombre legible (e.nombre): mientras id sea único por grupo, incluir nombre en el SELECT es seguro porque hay un único nombre por grupo. Para máxima portabilidad, muchos lo añaden también al GROUP BY.
Puedes agrupar por una expresión, no solo por una columna: GROUP BY strftime('%Y-%m', fecha) agrupa por mes; GROUP BY categoria_id agrupa por categoría.
SELECT departamento_id, COUNT(*) AS empleados, ROUND(AVG(salario), 0) AS salario_prom FROM empleados GROUP BY departamento_id
-- Revenue por vendedor: el clásico ranking comercial.
SELECT
e.nombre,
COUNT(DISTINCT p.id) AS pedidos,
ROUND(SUM(d.cantidad * d.precio_unitario * (1 - d.descuento)), 2) AS revenue
FROM detalle_pedidos d
JOIN pedidos p ON d.pedido_id = p.id
JOIN empleados e ON p.empleado_id = e.id
GROUP BY e.id
ORDER BY revenue DESC;
-- Conteo de pedidos por estado (cuántos hay de cada tipo).
SELECT estado, COUNT(*) AS num_pedidos
FROM pedidos
GROUP BY estado
ORDER BY num_pedidos DESC;
-- Salario promedio, mínimo y máximo por departamento (AVG con ROUND).
SELECT
dp.nombre AS departamento,
COUNT(*) AS empleados,
ROUND(AVG(e.salario), 2) AS salario_promedio,
MIN(e.salario) AS minimo,
MAX(e.salario) AS maximo
FROM empleados e
JOIN departamentos dp ON e.departamento_id = dp.id
GROUP BY dp.id
ORDER BY salario_promedio DESC;
-- Revenue por mes usando una expresión en el GROUP BY.
SELECT
strftime('%Y-%m', p.fecha) AS mes,
ROUND(SUM(d.cantidad * d.precio_unitario * (1 - d.descuento)), 2) AS revenue
FROM detalle_pedidos d
JOIN pedidos p ON d.pedido_id = p.id
GROUP BY mes
ORDER BY mes;
💡 Error frecuente: poner una columna en elSELECTque no está agregada ni en elGROUP BY. Otros motores (PostgreSQL, MySQL estricto) lo rechazan; SQLite devuelve un valor arbitrario de la columna. Sé explícito: agrega o agrupa.
| Elemento | Qué hace |
|---|---|
GROUP BY col | Una fila de salida por valor distinto de col |
GROUP BY a, b | Una fila por combinación distinta de a y b |
GROUP BY expr | Agrupa por una expresión (strftime, cálculo) |
WHERE | Filtra filas antes de agrupar (sin agregados) |
| Regla del SELECT | No agregado ⇒ debe estar en GROUP BY |
---
← Funciones de agregación: COUNT, SUM, AVG, MIN, MAXFiltrar grupos: HAVING →