SQL GROUP BY y funciones de agregación: resume tus datos

Published: 2026-04-08

GROUP BY colapsa varias filas que comparten el mismo valor en una o más columnas en una sola fila de resumen. Combinado con funciones de agregación como COUNT, SUM, AVG, MIN y MAX, es la base de casi todas las consultas de informes y analítica. GROUP BY está disponible en todas las bases de datos SQL, incluidas SQLite, PostgreSQL, MySQL, SQL Server y Oracle.

Datos de ejemplo

Usamos dos tablas en todos los ejemplos:

Tabla employees:

id name department hire_date salary
1 Alice Engineering 2023-01-15 95000
2 Bob Engineering 2023-06-01 85000
3 Carol Engineering 2024-03-10 85000
4 Dave Sales 2023-02-20 72000
5 Eve Sales 2023-09-05 68000
6 Frank Sales 2024-01-15 65000
7 Grace Marketing 2023-04-01 82000
8 Henry Marketing 2024-06-01 70000

Tabla orders:

id customer product category quantity unit_price order_date status
1 Acme Corp Laptop Pro Electronics 3 1299.99 2026-01-05 delivered
2 Acme Corp Wireless Mouse Electronics 10 29.99 2026-01-12 delivered
3 Globex Office Chair Furniture 5 249.99 2026-01-18 delivered
4 Globex Standing Desk Furniture 2 599.99 2026-01-20 shipped
5 Initech Notebook Pack Stationery 20 12.99 2026-02-03 delivered
6 Initech Pen Set Stationery 15 8.99 2026-02-07 delivered
7 Acme Corp Monitor 27" Electronics 4 399.99 2026-02-14 delivered
8 Umbrella Ltd Laptop Pro Electronics 2 1299.99 2026-02-20 cancelled
9 Umbrella Ltd Desk Lamp Furniture 8 39.99 2026-03-01 delivered
10 Globex Wireless Mouse Electronics 12 29.99 2026-03-05 shipped
11 Initech Monitor 27" Electronics 1 399.99 2026-03-10 delivered
12 Acme Corp Standing Desk Furniture 1 599.99 2026-03-15 processing
Explora el conjunto de datos en DbGate Lite

Sintaxis de GROUP BY

SELECT column1, aggregate_function(column2)
FROM table_name
WHERE condition
GROUP BY column1
HAVING aggregate_condition
ORDER BY column1;
  • GROUP BY: una o más columnas cuyas combinaciones de valores distintos definen cada grupo
  • Funciones de agregación: calculan un solo valor a partir de todas las filas del grupo
  • HAVING: filtra grupos (como WHERE, pero para resultados agrupados)
  • Cada columna en SELECT que no esté dentro de una agregación debe aparecer en GROUP BY

Orden de ejecución de SQL

Las cláusulas SQL se escriben en un orden pero se ejecutan en otro. Entender esto ayuda a explicar por qué HAVING puede hacer referencia a funciones de agregación pero WHERE no, y por qué no puedes usar un alias de SELECT en una cláusula HAVING.

FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
Paso Cláusula Qué sucede
1 FROM Carga la(s) tabla(s) y evalúa cualquier JOIN
2 WHERE Filtra filas individuales — las funciones de agregación no están permitidas aquí
3 GROUP BY Colapsa las filas restantes en grupos
4 HAVING Filtra grupos — las funciones de agregación están permitidas aquí
5 SELECT Calcula las columnas de salida y los alias
6 ORDER BY Ordena el conjunto de resultados final

Por eso WHERE status = 'delivered' se ejecuta antes del agrupamiento (rápido, filtro a nivel de fila), mientras que HAVING COUNT(*) > 2 se ejecuta después del agrupamiento (filtra grupos completos).

COUNT()

COUNT(*) cuenta todas las filas de un grupo. COUNT(column) cuenta los valores no NULL de esa columna.

Ejemplo: número de empleados por departamento

Ejemplo: número de pedidos por cliente

Ejemplo: contar entregados frente a otros estados

Cuenta solo los pedidos que coinciden con una condición usando COUNT con una expresión CASE:

SUM()

SUM(column) suma todos los valores del grupo para una columna numérica.

Ejemplo: salario total por departamento

Ejemplo: ingresos totales por categoría

Calcula los ingresos como quantity * unit_price:

Ejemplo: ingresos por cliente y categoría

Agrupa por varias columnas para desglosar más los ingresos:

AVG()

AVG(column) devuelve la media aritmética de todos los valores no NULL del grupo.

Ejemplo: salario promedio por departamento

Ejemplo: valor promedio de pedido por cliente

MIN() y MAX()

MIN(column) y MAX(column) devuelven los valores más pequeño y más grande del grupo.

Ejemplo: rango salarial por departamento

Ejemplo: primera y última fecha de pedido por cliente

Combinando múltiples agregaciones

Puedes combinar cualquier número de funciones de agregación en una sola consulta.

Ejemplo: resumen completo por departamento

Ejemplo: resumen de pedidos por cliente

HAVING

HAVING filtra grupos después de la agregación: es el equivalente de WHERE para resultados agrupados.

Ejemplo: departamentos con más de dos empleados

Ejemplo: clientes con ingresos totales superiores a 2000

Ejemplo: categorías con valor promedio de pedido inferior a 500

WHERE vs. HAVING

WHERE filtra filas individuales antes del agrupamiento. HAVING filtra grupos después de la agregación.

Ejemplo: ingresos solo de pedidos entregados por cliente

Usa WHERE para excluir filas antes de agrupar y luego HAVING para filtrar resultados:

GROUP BY con JOINs

Puedes agrupar datos que abarcan varias tablas uniéndolas primero.

Ejemplo: ingresos por departamento mediante JOIN con empleados

Ejemplo: número de pedidos e ingresos por categoría y mes

GROUP BY con expresiones

Puedes agrupar por expresiones calculadas, no solo por columnas simples.

Ejemplo: empleados contratados por año

Ejemplo: pedidos por mes

COUNT(DISTINCT)

COUNT(DISTINCT column) cuenta el número de valores únicos del grupo.

Ejemplo: productos únicos pedidos por cliente

Filtrar grupos con múltiples condiciones

HAVING admite cualquier expresión booleana, incluidas combinaciones AND/OR.

Ejemplo: clientes activos de alto valor

Encuentra clientes con más de un pedido e ingresos totales superiores a 1000:

Ejemplo práctico: informe de ventas

Combina todo en un informe completo de resumen de ventas.

Ejemplo: informe de rendimiento por categoría

Ejemplo: producto principal por ingresos

GROUP BY vs. funciones de ventana

GROUP BY colapsa filas en una fila por grupo. Las funciones de ventana mantienen todas las filas y añaden una columna calculada; consulta el tutorial de funciones de ventana en SQL para más detalles.

Ejemplo: GROUP BY devuelve una fila por departamento

Ejemplo: una función de ventana mantiene todas las filas

Casos de uso comunes de GROUP BY

  • Paneles de informes: resume totales, promedios y conteos por categoría o periodo de tiempo
  • Análisis de dotación: cuenta empleados, usuarios o registros por grupo
  • Desglose de ingresos: ingresos totales y promedio por producto, región o cliente
  • Análisis de tendencias: agrupa por mes o año para detectar patrones a lo largo del tiempo
  • Top-N por grupo: combina con subconsultas o CTE para encontrar el mejor desempeño en cada grupo
  • Validación de datos: cuenta duplicados o valores NULL en los grupos
  • Análisis de cohortes: agrupa usuarios por fecha de registro o segmento de comportamiento

Consejos de rendimiento

  1. Indexa las columnas de GROUP BY: los índices en las columnas agrupadas aceleran el paso de ordenación y agrupamiento
  2. Filtra pronto con WHERE: reducir filas antes de agrupar con WHERE es más rápido que filtrar después con HAVING
  3. Evita SELECT *: selecciona solo las columnas que realmente necesitas; las operaciones de agregación se benefician de datos más estrechos
  4. Usa índices cubrientes: un índice que incluya tanto la columna de GROUP BY como la columna agregada puede evitar un escaneo completo de la tabla
  5. Considera vistas materializadas: para agregados calculados con frecuencia en tablas grandes, algunas bases de datos admiten resúmenes precomputados

Resumen

  • GROUP BY colapsa filas que comparten los mismos valores de columna en una fila de resumen por combinación única
  • COUNT(*) cuenta todas las filas; COUNT(column) cuenta valores no NULL; COUNT(DISTINCT column) cuenta valores únicos
  • SUM suma valores numéricos; AVG calcula la media; MIN / MAX encuentran los límites
  • Cada columna de SELECT debe aparecer en GROUP BY o estar envuelta en una función de agregación
  • WHERE filtra filas antes del agrupamiento; HAVING filtra grupos después de la agregación
  • Puedes usar GROUP BY con expresiones (por ejemplo, SUBSTR(date, 1, 7) para agrupar a nivel de mes)
  • Unir tablas antes de agrupar te permite combinar datos de múltiples fuentes en un solo informe
  • Usa funciones de ventana en lugar de GROUP BY cuando necesites métricas a nivel de grupo sin perder filas individuales

Prueba a combinar GROUP BY con JOINs, expresiones CASE y cláusulas HAVING para crear los informes que tu análisis de datos necesita.