GROUP BY en SQL: qué es, cómo se usa y ejemplos prácticos

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;
  1. Se seleccionan clientes activos (WHERE activo = 1).
  2. Se agrupan por país.
  3. Se devuelven solo países con más de 10 clientes (HAVING).

Errores comunes con GROUP BY

  1. 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)).
  1. 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;
  1. 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;
  1. Esperar que GROUP BY ordene automáticamente
    En algunos motores parecía así, pero no es estándar.
    Siempre usar ORDER BY si necesitás resultados ordenados.
  1. Creer que COUNT(*) ignora filas NULL
  • COUNT(*): cuenta todas las filas.
  • COUNT(columna): solo filas con la columna distinta de NULL.

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 WHERE y luego con HAVING.
  • 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/CUBE en 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.

Scroll al inicio