Repaso integrado · URL histórica conservada

Repaso práctico de GROUP BY y HAVING

Une filtros, agregaciones y condiciones sobre grupos sin confundir el orden lógico de la consulta.

30–35 minNivel intermedioAntes necesitas: WHERE, funciones de agregación, GROUP BY y HAVING

Al terminar, podrás

  • Distinguir qué condición pertenece a WHERE y cuál a HAVING.
  • 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.

EtapaUnidad de una filaPregunta de control
Antes de agruparUn pedido¿La condición puede evaluarse con una sola fila?
Después de agruparUn estado¿Qué columnas identifican el grupo?
Después de agregarUn 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:

SQL de control
SELECT COUNT(*) AS filas_validas
FROM pedidos
WHERE estado <> 'cancelado';
Resultado de la comprobación previa
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.

SQL
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;
Resultado con la base de datos educativa
estadopedidostotal_facturado
enviado91818.70
pendiente6840.30
  1. WHERE elimina los pedidos cancelados antes de contar.
  2. GROUP BY estado crea un grupo para cada estado restante.
  3. COUNT y SUM producen un resumen por grupo.
  4. HAVING comprueba 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.

SQL
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;
Categorías que superan el umbral
categoriaproductosprecio_medio
Ordenadores1999.00
Pantallas2214.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 WHERE las 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

  • WHERE filtra filas antes de agrupar.
  • HAVING filtra grupos después de calcular agregados.
  • Cada columna no agregada del resultado debe describir el grupo de forma válida.

Recorrido recomendado

Fuentes técnicas consultadas