Aprende leyendo en orden

GROUP BY y HAVING — Agregados por grupo

Aprende SQL GROUP BY y HAVING: agregados por grupo, filtrar grupos con HAVING y en qué se diferencia de WHERE — practicando con datos de notas en CSV, en vivo en tu navegador.

Los datos usados en este artículo — la tabla score

En el artículo anterior redujiste una tabla entera a un solo valor. El `GROUP BY` de este artículo (agrupación — reunir en un mismo grupo las filas que comparten el mismo valor) ejecuta los agregados por grupo y devuelve una fila de agregado por grupo. Además, `HAVING` te permite filtrar los resultados de agregado, como «solo los grupos cuyo promedio sea 80 o más».

El tema es la misma tabla score que la vez anterior (30 filas = 10 personas × 3 materias). Agrupar por name da 10 grupos, y agrupar por subject da 3 grupos.

Antes de meterte en los ejercicios, echa un vistazo a las definiciones de columnas y a una muestra de los datos de la tabla score.

① Usa PRAGMA table_info(score); para comprobar los nombres de columna, los tipos y la clave primaria.

② Usa SELECT * FROM score LIMIT 5; para previsualizar las 5 primeras filas de datos.

Editor SQL

Ejecutar una consulta para ver el resultado

GROUP BY — agregar por grupo

Cuando escribes GROUP BY columna, las filas que comparten el mismo valor en esa columna se reúnen en un mismo grupo, y las funciones de agregado se calculan una vez por grupo. Con SELECT name, AVG(score) FROM score GROUP BY name;, las 3 filas con el mismo nombre (una por materia) forman un grupo, y obtienes una fila de promedio por nombre. Las únicas «columnas planas» que puedes poner en SELECT son las columnas nombradas en `GROUP BY`; cualquier otra columna tiene que ir envuelta en una función de agregado (porque su valor no es único dentro de un grupo).

GROUP BY name
Filas originales (30)GROUP BY nameAgregado de grupoAlice Math 92Alice English 85Alice Science 78Grupo AliceAlice / AVG 85.0
Las 3 filas con el mismo nombre (una por materia) se reducen a un solo grupo, y AVG(score) se calcula por grupo. Con 10 personas, el resultado son 10 filas.
-- Cantidad / promedio / nota más alta por materia (3 grupos por subject)
SELECT
  subject,
  COUNT(*)             AS row_count,
  ROUND(AVG(score), 1) AS avg_score,
  MAX(score)           AS top_score
FROM score
GROUP BY subject;

Imagina el requisito «quiero una lista del promedio de cada estudiante». (Ejecútalo correctamente y aparecerá la explicación.)

① Agrupa la tabla score por nombre.

② Para cada grupo, extrae el nombre como name, el número de materias rendidas como subjects y el promedio como avg_score, en ese orden.

③ Redondea el promedio a 2 decimales. El orden no está especificado.

Editor SQL

Ejecutar una consulta para ver el resultado

HAVING — filtrar los resultados de agregado

Tras un agregado de grupo, a veces quieres filtrar sobre el valor de agregado, como «muestra solo los grupos cuyo promedio sea 80 o más». No puedes hacer esto con WHERE. WHERE es una cláusula que juzga las filas individuales antes de la agrupación, así que un agregado de grupo como AVG(score) aún no se ha calculado. Ahí es donde entra `HAVING`. HAVING se escribe después de GROUP BY y juzga su condición sobre los resultados de agregado de los grupos. Escribir HAVING AVG(score) >= 80 conserva solo los grupos cuyo promedio es 80 o más.

Tras GROUP BY, filtrar con HAVING
score 30 filasGROUP BY name(10 grupos)HAVINGAVG(score) >= 80Quedan 6 gruposJuzgado tras agregar
GROUP BY agrega en 10 grupos, luego HAVING AVG(score) >= 80 juzga los grupos ya agregados y conserva solo los que satisfacen la condición.
-- Solo los nombres cuyo promedio supera 85 (HAVING juzga el valor de agregado)
SELECT
  name,
  ROUND(AVG(score), 2) AS avg_score
FROM score
GROUP BY name
HAVING AVG(score) > 85;

Imagina el requisito «quiero listar solo los mejores estudiantes con un promedio de 80 o más».

① Agrupa la tabla score por nombre.

② Para cada grupo, extrae el nombre como name y el promedio como avg_score (redondeado a 2 decimales).

③ Conserva solo los grupos cuyo promedio sea 80 o más. El orden no está especificado.

Editor SQL

Ejecutar una consulta para ver el resultado

WHERE vs. HAVING — filtrar filas, o filtrar grupos

WHERE y HAVING son ambos filtros, pero actúan en momentos distintos. WHERE juzga las filas individuales y las descarta antes de la agrupación. HAVING juzga por grupo y los descarta después de agrupar y agregar. El orden de ejecución es FROMWHEREGROUP BYHAVINGSELECTORDER BY.

También puedes usar ambos a la vez. En ese caso el flujo es «restringir las filas objetivo con WHERE, luego agrupar, luego filtrar los resultados de agregado con HAVING». Por ejemplo, para obtener «las personas cuyo promedio por nombre sea 80 o más, considerando solo Math y English», restringes las filas con WHERE subject IN ('Math','English') y restringes los grupos con HAVING AVG(score) >= 80.

WHERE filtra filas, HAVING filtra grupos
Filas originales (30)Las filas que fallancaen aquíWHEREjuzga filassobre columnas planasGROUP BY nameagrupa las filasHAVINGjuzga grupossobre valores agregadosLos grupos que fallancaen aquíLos grupos restantesson el resultadoTodas las filas entranFilas descartadasSolo filas restantesTras agregarGrupos descartadosGrupos restantes
WHERE juzga las filas sobre columnas planas y las descarta antes de la agrupación, mientras que HAVING juzga los grupos sobre valores de agregado (AVG, etc.) y los descarta después de la agrupación. El punto clave es dónde se descartan las cosas. Ejemplo: WHERE subject='Math' (filtra filas) y HAVING AVG(score)>=80 (filtra grupos).
-- Usar WHERE y HAVING juntos
-- Excluir Science, conservar solo los nombres cuyo promedio sea 80 o más
SELECT
  name,
  ROUND(AVG(score), 2) AS avg_two
FROM score
WHERE subject <> 'Science'
GROUP BY name
HAVING AVG(score) >= 80;

Imagina el requisito «considerando solo las dos materias Math y English, quiero las personas cuyo promedio por nombre sea 85 o más».

① De la tabla score, conserva solo las filas donde subject sea Math o English (Science queda fuera de alcance).

② Agrupa las filas restantes por nombre y extrae el nombre como name y el promedio de las dos materias como avg_two (redondeado a 2 decimales).

③ Luego conserva solo los grupos cuyo promedio sea 85 o más. El orden no está especificado.

Editor SQL

Ejecutar una consulta para ver el resultado

Imagina el requisito «quiero mostrar los promedios por materia como un ranking, el más alto primero».

① Agrupa la tabla score por materia.

② Para cada grupo, extrae la materia como subject, el número de estudiantes que la rindieron como students y el promedio como avg_score (redondeado a 2 decimales), en ese orden.

③ Ordénalos del promedio más alto al más bajo.

Editor SQL

Ejecutar una consulta para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1Cuando especificas GROUP BY name, ¿cuántas filas tiene el resultado (tabla score / 10 nombres distintos)?

Pregunta 2Cuando quieres extraer «solo las personas cuyo promedio por nombre sea 80 o más», ¿en qué cláusula va la condición de 80 o más?

Pregunta 3¿Qué afirmación describe correctamente la diferencia entre WHERE y HAVING?