GROUP BY agrupa y colapsa filas: produce una fila de salida por grupo. Las funciones de agregación (COUNT, SUM, AVG, MAX, MIN) resumen cada grupo. Aquí tienes los patrones que vas a reutilizar y el error con el que acabarás tropezando.
🎙️ Publicado y grabado:
-- ¿cuántos pedidos tiene cada cliente?
SELECT user_id, COUNT(*) AS orders
FROM orders
GROUP BY user_id;
-- ingresos por cliente, de mayor a menor, los 10 primeros
SELECT user_id, SUM(amount) AS revenue
FROM orders
GROUP BY user_id
ORDER BY revenue DESC
LIMIT 10;
-- varias agregaciones a la vez: la consulta de informes
SELECT user_id,
COUNT(*) AS orders,
SUM(amount) AS revenue,
AVG(amount) AS avg_order,
MAX(amount) AS biggest
FROM orders
GROUP BY user_id;
-- agrupar por un valor calculado: pedidos por mes
SELECT strftime('%Y-%m', created_at) AS month, -- SQLite;
COUNT(*) -- Postgres: date_trunc
FROM orders
GROUP BY month;
SELECT user_id, name, SUM(amount) -- ← name es el problema
FROM orders JOIN users ON ...
GROUP BY user_id;
ERROR: column "users.name" must appear in the GROUP BY
clause or be used in an aggregate function
Por qué ocurre: después de colapsar las filas, un grupo de 50 filas contiene 50 valores de name; la base de datos se niega a elegir uno por ti. Solución: añádelo a la clave del grupo (GROUP BY user_id, name; está bien porque el nombre es constante para cada usuario) o envuélvelo en una función de agregación (MAX(name), la forma de decir «sé que es constante»).
-- clientes que gastaron 100 o más (condición sobre el GRUPO → HAVING)
SELECT user_id, SUM(amount) AS total
FROM orders
WHERE created_at >= '2026-01-01' -- filtro de filas: ANTES de agrupar
GROUP BY user_id
HAVING SUM(amount) >= 100; -- filtro de grupos: DESPUÉS
-- la pista: si la condición contiene SUM/COUNT, no puede ir en WHERE
SELECT email, COUNT(*)
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
-- cada email duplicado con su recuento. sirve para cualquier columna.
COUNT(col) omite los NULL de esa columna. Los resultados son distintos y ambos son «correctos». ¿Quieres contar pedidos? Usa el asterisco. ¿Pedidos con cupón? Usa COUNT(coupon). NULL forma su propio grupo: las filas con NULL en la clave de agrupación se juntan en un único grupo NULL. Esa fila misteriosa al principio del informe no es un error: son tus datos ausentes llamando la atención.