Imagina que eres el gerente de una tienda. No te interesa tanto saber que “Juan compró un lápiz a las 10:00” y que “Ana compró una goma a las 10:05”.
Lo que realmente quieres saber es:
¿Cuánto hemos vendido en total hoy?
¿Cuántos productos ha vendido cada empleado?
Para responder a estas preguntas, necesitamos colapsar cientos de filas individuales en un solo resultado resumido. En T-SQL, la herramienta para hacer esto es la cláusula GROUP BY.
Si alguna vez has usado las Tablas dinámicas (Pivot Tables) en Excel, el concepto es exactamente el mismo: tomar un montón de datos y agruparlos por una característica común.
Sintaxis y lógica
La cláusula GROUP BY se coloca después del WHERE (si lo hay) y antes del ORDER BY.
SELECT ColumnaAgrupacion, FUNCION_AGREGADA(Columna)
FROM Tabla
GROUP BY ColumnaAgrupacion;
Cuando SQL Server ejecuta esto, hace lo siguiente:
- Recorre la tabla.
- Identifica los valores únicos de la columna por la que agrupamos (ej:
Ciudad). - Crea “cubos” o grupos virtuales para cada valor único.
- Calcula el resumen para cada grupo.
Veámoslo con un ejemplo. Supongamos que tenemos una tabla Empleados y queremos saber cuántos empleados hay en cada departamento.
SELECT Departamento, COUNT(*) AS NumeroEmpleados
FROM Empleados
GROUP BY Departamento;
El resultado sería el siguiente,
| Departamento | NumeroEmpleados |
|---|---|
| Ventas | 10 |
| IT | 5 |
| RRHH | 2 |
SQL Server ha cogido todas las filas donde Departamento es ‘Ventas’, las ha juntado, y las ha contado.
El error Column invalid
Este es el concepto más importante de este artículo y la fuente del 90% de los errores. El famoso error:
Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
Cuando usas GROUP BY, en el SELECT SOLO puedes poner:
- Columnas que aparezcan en el
GROUP BY. - Funciones de agregación (
COUNT,SUM,MAX, etc.).
¿Por qué?
Cuesta verlo al principio, pero cuando lo veas tiene todo el sentido.
Cuando agrupas por Departamento, en la agrupación los campos no seleccionados desaparecen. El grupo no los tiene (no los podría tener, porque hay varios).
Supongamos que en el departamento de ‘Ventas’ tienes a Juan Gomez, a Pedro García y a María Gutierrez… si intentas hacer esto:
-- ⛔ ESTO DARÁ ERROR
SELECT Departamento, Apellido, COUNT(*)
FROM Empleados
GROUP BY Departamento;
SQL Server te dirá:
Me pides el “Nombre” del departamento ‘Ventas’
Pero tengo varios nombres diferentes (Gomez, García, Gutierrez).
Si quieres mostrar el nombre, o bien lo agregas al GROUP BY (rompiendo el grupo) o usas una función agregada (como MAX(Nombre)).
No funcionará incluso aunque todos los elementos del grupo tengan la misma propiedad igual.
Al final es un problema de concepto. Intentas meter una casilla en varias, y eso no puede hacerse (sin agregar).
Agrupando por múltiples columnas
Podemos crear grupos más específicos combinando varias columnas. Por ejemplo, queremos saber cuántos empleados hay en cada departamento, pero desglosado por Ciudad.
SELECT Departamento, Ciudad, COUNT(*) AS Total
FROM Empleados
GROUP BY Departamento, Ciudad;
Aquí, SQL Server creará un grupo único para cada combinación.
- Ventas - Madrid
- Ventas - Barcelona
- IT - Madrid
- IT - Valencia
Solo se agruparán las filas que compartan ambos valores.
Orden de ejecución GROUP BY con WHERE
Es vital entender el orden de ejecución que hemos mencionado en módulos anteriores.
FROM (Carga datos)
WHERE (Filtra filas individuales)
GROUP BY (Agrupa las filas que sobrevivieron)
SELECT (Muestra resultados)
Esto significa que el WHERE se aplica antes de crear los grupos.
Por ejemplo, queremos contar los empleados por departamento, pero solo contando a los que están “Activos”.
SELECT Departamento, COUNT(*) AS EmpleadosActivos
FROM Empleados
WHERE Activo = 1 -- Primero filtramos a los inactivos
GROUP BY Departamento; -- Luego agrupamos a los que quedan
Tratamiento de nulos
En el mundo de GROUP BY, todos los NULL se consideran iguales.
Si agrupas por una columna que tiene valores nulos, SQL Server creará un único grupo para los NULL, en lugar de ignorarlos o separarlos.
SELECT JefeID, COUNT(*)
FROM Empleados
GROUP BY JefeID;
Si hay 5 empleados que no tienen jefe (JefeID es NULL), verás una fila en el resultado donde JefeID es NULL y el conteo es 5.
