Repaso práctico de GROUP BY y HAVING
Une filtros, agregaciones y condiciones sobre grupos sin confundir el orden lógico de la consulta.
Al terminar, podrás
- Distinguir qué condición pertenece a
WHEREy cuál aHAVING. - Explicar qué representa cada fila después de agrupar.
- Comprobar una consulta por etapas antes de añadir más cláusulas.
El modelo mental: filas primero, grupos después
WHERE decide qué filas llegan a la agrupación. GROUP BY reúne las filas que comparten un valor. Las funciones como COUNT y SUM calculan un resumen por grupo. Por último, HAVING conserva o descarta grupos completos.
Pregunta de control: si la condición puede evaluarse mirando una sola fila original, normalmente pertenece a WHERE. Si necesita un resultado agregado, pertenece a HAVING.
Antes de escribir: define la unidad de cada fila
Una consulta agregada cambia la unidad del resultado. La tabla pedidos contiene una fila por pedido, pero después de GROUP BY estado cada fila representa un estado. Decirlo en voz alta evita seleccionar columnas que ya no describen el grupo.
| Etapa | Unidad de una fila | Pregunta de control |
|---|---|---|
| Antes de agrupar | Un pedido | ¿La condición puede evaluarse con una sola fila? |
| Después de agrupar | Un estado | ¿Qué columnas identifican el grupo? |
| Después de agregar | Un resumen por estado | ¿Qué cálculo decide si el grupo permanece? |
Comprueba primero las filas de entrada
Antes de construir el resumen, una consulta de control confirma que el filtro deja quince pedidos:
SELECT COUNT(*) AS filas_validas
FROM pedidos
WHERE estado <> 'cancelado';| filas_validas |
|---|
| 15 |
Ejemplo verificado paso a paso
Queremos resumir los pedidos no cancelados por estado y mostrar únicamente los estados que tengan al menos cinco pedidos.
SELECT estado,
COUNT(*) AS pedidos,
ROUND(SUM(total), 2) AS total_facturado
FROM pedidos
WHERE estado <> 'cancelado'
GROUP BY estado
HAVING COUNT(*) >= 5
ORDER BY total_facturado DESC;| estado | pedidos | total_facturado |
|---|---|---|
| enviado | 9 | 1818.70 |
| pendiente | 6 | 840.30 |
WHEREelimina los pedidos cancelados antes de contar.GROUP BY estadocrea un grupo para cada estado restante.COUNTySUMproducen un resumen por grupo.HAVINGcomprueba el recuento agregado y conserva los grupos válidos.
No elijas columnas arbitrarias del grupo
Una columna del resultado debe estar agregada, aparecer en GROUP BY o depender funcionalmente de las columnas agrupadas cuando el motor puede demostrarlo. Para una ruta portable, la regla más fácil de revisar es explícita: agrupa todas las columnas descriptivas que quieras mostrar.
No confíes en una fila elegida al azar. SQLite documenta el concepto de columna «desnuda» en consultas agregadas, mientras que MySQL con ONLY_FULL_GROUP_BY rechaza muchas selecciones ambiguas. Una consulta educativa debe expresar qué valor representa al grupo, no depender de una extensión del motor.
Transferencia: aplica el mismo método a categorías
Ahora cada fila final representa una categoría. El filtro de grupos conserva únicamente las categorías cuyo precio medio alcanza 100.
SELECT c.nombre AS categoria,
COUNT(*) AS productos,
ROUND(AVG(p.precio), 2) AS precio_medio
FROM categorias AS c
JOIN productos AS p
ON p.categoria_id = c.id
GROUP BY c.id, c.nombre
HAVING AVG(p.precio) >= 100
ORDER BY precio_medio DESC, categoria;| categoria | productos | precio_medio |
|---|---|---|
| Ordenadores | 1 | 999.00 |
| Pantallas | 2 | 214.00 |
Se agrupa por el identificador y el nombre para que la identidad de la categoría quede explícita. HAVING utiliza el promedio sin redondear; el redondeo se reserva para presentar el resultado.
Error frecuente: usar HAVING para todo
Una consulta más difícil de leer
SELECT estado, COUNT(*)
FROM pedidos
GROUP BY estado
HAVING estado <> 'cancelado';Aunque algunos motores aceptan condiciones no agregadas en HAVING, el filtro pertenece conceptualmente a las filas originales. Colocarlo en WHERE expresa mejor la intención y evita depender de extensiones particulares.
Comprueba tu dominio
- Puedes explicar qué representa una fila antes y después de agrupar.
- Sabes mover a
WHERElas condiciones que pertenecen a filas originales. - No seleccionas columnas ambiguas que no identifican el grupo.
- Compruebas las filas de entrada y el número de grupos antes de confiar en el resultado final.
Qué debes recordar
WHEREfiltra filas antes de agrupar.HAVINGfiltra grupos después de calcular agregados.- Cada columna no agregada del resultado debe describir el grupo de forma válida.