En SQL, la cláusula GROUP BY se utiliza para agrupar filas que comparten uno o más valores en común y aplicar sobre ellas funciones de agregación como SUM(), COUNT(), AVG(), MAX() o MIN().
Es una herramienta fundamental para crear reportes resumidos: cantidades por categoría, promedios por cliente, ventas por mes, etc.
👉 Sin GROUP BY, cada consulta trabaja a nivel de filas individuales. Con GROUP BY, trabajamos a nivel de grupos de filas.
Sintaxis básica de GROUP BY
SELECT columna1, columna2, funcion_agregada(columna3)
FROM tabla
WHERE condiciones
GROUP BY columna1, columna2
HAVING condiciones_de_grupo
ORDER BY columna1;
- columnas de agrupación: definen los grupos.
- función agregada: calcula un valor por cada grupo.
- HAVING: filtra grupos (no filas individuales).
- ORDER BY: ordena los resultados agrupados.
Ejemplos prácticos de GROUP BY
1) Contar clientes por país
SELECT pais, COUNT(*) AS total_clientes
FROM clientes
GROUP BY pais;
👉 Devuelve el número de clientes en cada país.
2) Ventas totales por cliente
SELECT cliente_id, SUM(total) AS ventas_totales
FROM pedidos
GROUP BY cliente_id;
👉 Cada cliente aparece con el total de sus pedidos.
3) Promedio de salarios por departamento
SELECT departamento, AVG(salario) AS salario_promedio
FROM empleados
GROUP BY departamento;
👉 Muestra el salario promedio de cada departamento.
4) Agrupar por múltiples columnas
SELECT departamento, puesto, COUNT(*) AS empleados
FROM empleados
GROUP BY departamento, puesto;
👉 Cuenta cuántos empleados hay por combinación de departamento y puesto.
5) Ventas por año
SELECT YEAR(fecha) AS anio, SUM(total) AS ventas
FROM pedidos
GROUP BY YEAR(fecha)
ORDER BY anio;
👉 Ventas totales por año.
6) Filtrar grupos con HAVING
SELECT cliente_id, SUM(total) AS ventas
FROM pedidos
GROUP BY cliente_id
HAVING SUM(total) > 1000;
👉 Solo devuelve clientes con ventas superiores a 1000.
GROUP BY sin funciones de agregación
Aunque lo más común es usarlo con agregaciones, también se puede usar solo para devolver valores distintos de columnas. Es como un DISTINCT más explícito:
SELECT pais
FROM clientes
GROUP BY pais;
👉 Devuelve los países únicos de la tabla.
Diferencias importantes: WHERE vs HAVING
- WHERE filtra filas antes de agrupar.
- HAVING filtra grupos después de agrupar.
Ejemplo combinado:
SELECT pais, COUNT(*) AS clientes
FROM clientes
WHERE activo = 1
GROUP BY pais
HAVING COUNT(*) > 10;
- Se seleccionan clientes activos (
WHERE activo = 1). - Se agrupan por país.
- Se devuelven solo países con más de 10 clientes (
HAVING).
Errores comunes con GROUP BY
- Seleccionar columnas no agrupadas ni agregadas
-- ❌ Error: nombre no está ni en GROUP BY ni en función agregada
SELECT pais, nombre FROM clientes GROUP BY pais;
Solución:
- Incluir la columna en el
GROUP BY. - O aplicar una función de agregación sobre ella (
MAX(nombre)).
- Confundir HAVING con WHERE
-- ❌ Ineficiente
SELECT pais, COUNT(*)
FROM clientes
GROUP BY pais
HAVING pais = 'Argentina';
Mejor usar WHERE antes:
SELECT pais, COUNT(*)
FROM clientes
WHERE pais = 'Argentina'
GROUP BY pais;
- Olvidar agrupar todas las columnas necesarias
-- ❌ Error en motores estrictos
SELECT departamento, puesto, AVG(salario)
FROM empleados
GROUP BY departamento;
Debe agruparse también puesto:
SELECT departamento, puesto, AVG(salario)
FROM empleados
GROUP BY departamento, puesto;
- Esperar que GROUP BY ordene automáticamente
En algunos motores parecía así, pero no es estándar.
Siempre usarORDER BYsi necesitás resultados ordenados.
- Creer que COUNT(*) ignora filas NULL
COUNT(*): cuenta todas las filas.COUNT(columna): solo filas con la columna distinta deNULL.
GROUP BY y funciones de agregación más usadas
COUNT(*)→ número de filas.COUNT(col)→ número de valores no nulos.SUM(col)→ suma.AVG(col)→ promedio.MAX(col)/MIN(col)→ valores extremos.- Funciones avanzadas:
STRING_AGG(),GROUP_CONCAT(),ARRAY_AGG()(según motor).
GROUP BY ROLLUP, CUBE y GROUPING SETS (avanzado)
Muchos motores (PostgreSQL, SQL Server, Oracle) permiten extensiones para generar totales y subtotales automáticamente.
Ejemplo con ROLLUP
SELECT departamento, puesto, SUM(salario) total
FROM empleados
GROUP BY ROLLUP (departamento, puesto);
👉 Devuelve:
- Totales por (departamento, puesto).
- Totales por departamento.
- Total general.
Ejemplo con CUBE
SELECT departamento, puesto, SUM(salario) total
FROM empleados
GROUP BY CUBE (departamento, puesto);
👉 Devuelve todas las combinaciones posibles de totales.
GROUP BY con expresiones y funciones
Podés agrupar por expresiones, no solo por columnas:
SELECT YEAR(fecha) AS anio, MONTH(fecha) AS mes, SUM(total) ventas
FROM pedidos
GROUP BY YEAR(fecha), MONTH(fecha);
Incluso por fórmulas:
SELECT ROUND(precio, -2) AS rango, COUNT(*) productos
FROM productos
GROUP BY ROUND(precio, -2);
Buenas prácticas con GROUP BY
- Usar alias claros en funciones agregadas (
SUM(total) AS ventas). - Filtrar primero con
WHEREy luego conHAVING. - Evitar agrupar por demasiadas columnas (puede explotar combinaciones).
- Aprovechar índices en las columnas de agrupación.
- Con tablas grandes, validar si conviene precalcular agregados (vistas materializadas, particiones).
- Usar
ROLLUP/CUBEen reportes de negocio para subtotales automáticos.
Ejemplo avanzado: top clientes por ventas
SELECT c.nombre, SUM(p.total) AS ventas
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
WHERE p.fecha >= '2025-01-01'
GROUP BY c.id, c.nombre
HAVING SUM(p.total) > 5000
ORDER BY ventas DESC
LIMIT 10;
👉 Top 10 clientes con más de 5000 en compras en 2025.
Preguntas frecuentes (FAQ)
1. ¿Puedo usar GROUP BY sin funciones agregadas?
Sí, pero el resultado es equivalente a un DISTINCT.
2. ¿Qué diferencia hay entre GROUP BY y DISTINCT?
DISTINCT: devuelve filas únicas.GROUP BY: agrupa y permite aplicar funciones agregadas.
3. ¿Qué pasa si agrupo por una columna con NULLs?
Todos los NULL se agrupan juntos como un único valor.
4. ¿HAVING reemplaza a WHERE?
No. WHERE filtra filas antes de agrupar; HAVING filtra grupos ya formados.
5. ¿Qué motores soportan ROLLUP y CUBE?
PostgreSQL, SQL Server, Oracle, DB2. MySQL soporta ROLLUP.
Conclusión
La cláusula GROUP BY en SQL es la herramienta esencial para pasar de datos crudos a reportes resumidos y análisis agregados. Permite agrupar filas, aplicar funciones de agregación y crear reportes poderosos.
Dominar GROUP BY junto con HAVING, ROLLUP y CUBE es clave para trabajar en analítica de datos, reportes empresariales y BI.
Aprende a agrupar y resumir datos en GROUP BY y HAVING y consulta el Glosario SQL completo.