WHERE vs HAVING en SQL: filtra filas y grupos correctamente
WHERE filtra filas individuales antes de agrupar. HAVING filtra grupos después de la agregación. Combina ambos cuando un informe necesita una condición sobre cada transacción y otra sobre los totales.
“Regiones con al menos 150 en ventas pagadas” contiene dos reglas: solo cuentan las transacciones pagadas y solo se muestran las regiones cuyo total llega a 150. Colocar ambas condiciones en el mismo lugar puede cambiar el significado o producir una consulta inválida.
| Condición | Cláusula |
|---|---|
| Conservar transacciones pagadas | WHERE status = ‘paid’ |
| Conservar grupos con un total mínimo de 150 | HAVING SUM(amount) >= 150 |
| Ordenar los grupos finales | ORDER BY |
Razona con estas etapas lógicas: FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY. Sirven para entender la consulta; no describen necesariamente el orden físico elegido por el optimizador.
Pruébalo en SQLite
Abre SQL Lab, pega esta preparación y ejecútala una vez. Después ejecuta cada consulta por separado. Los nombres de tablas y columnas se mantienen en inglés para usar el mismo SQL en todos los idiomas. La interfaz del laboratorio y las lecciones enlazadas están en inglés.
CREATE TABLE blog_sales (
id INTEGER PRIMARY KEY, region TEXT, status TEXT, amount INTEGER
);
INSERT INTO blog_sales VALUES
(1, 'North', 'paid', 100), (2, 'North', 'paid', 80),
(3, 'North', 'refunded', 500), (4, 'South', 'paid', 70),
(5, 'South', 'paid', 90), (6, 'West', 'paid', 40);
1. Combina filtros de filas y grupos
North suma 180 en ventas pagadas, no 680: su transacción reembolsada no debe contribuir. South suma 160 y West 40. WHERE elimina el reembolso antes de agrupar; HAVING elimina después el grupo West.
SELECT region, SUM(amount) AS paid_total, COUNT(*) AS paid_orders
FROM blog_sales
WHERE status = 'paid'
GROUP BY region
HAVING SUM(amount) >= 150
ORDER BY region;
Resultado esperado
| region | paid_total | paid_orders |
|---|---|---|
| North | 180 | 2 |
| South | 160 | 2 |
2. Un umbral por fila elimina transacciones
Añadir amount >= 80 a WHERE elimina la transacción pagada de 70 en South. Su total pasa a 90. La pregunta ahora es el total de transacciones individuales grandes, no las regiones con totales grandes.
SELECT region, SUM(amount) AS total
FROM blog_sales
WHERE status = 'paid' AND amount >= 80
GROUP BY region
ORDER BY region;
Resultado esperado
| region | total |
|---|---|
| North | 180 |
| South | 90 |
3. Un umbral por grupo conserva las filas contribuyentes
Al colocar la condición en HAVING, las dos transacciones pagadas de South contribuyen y el total sigue siendo 160. West queda fuera porque su total es inferior a 80. Condiciones parecidas pueden responder preguntas de negocio distintas.
SELECT region, SUM(amount) AS total
FROM blog_sales
WHERE status = 'paid'
GROUP BY region
HAVING SUM(amount) >= 80
ORDER BY region;
Resultado esperado
| region | total |
|---|---|
| North | 180 |
| South | 160 |
Practica y comprueba tu respuesta
Obtén las regiones con al menos dos transacciones pagadas. Conserva WHERE status = 'paid', agrupa por region y usa HAVING COUNT(*) >= 2. La respuesta es North y South, con dos transacciones cada una.
Para depurar, revisa primero las filas que sobreviven a WHERE, ejecuta la agregación sin HAVING y añade después el filtro de grupos. Así distingues una pérdida de datos causada por el filtro de filas de otra causada por un umbral agregado.
No coloques SUM(amount) en WHERE en esta consulta agrupada: el total todavía no está disponible. Tampoco selecciones una columna como id si no está agrupada ni agregada. Los motores estrictos rechazan la consulta; los permisivos pueden devolver un valor que no representa al grupo.
Usa la expresión agregada en HAVING en lugar del alias de SELECT para mejorar la portabilidad. HAVING no sustituye todos los filtros WHERE. Una condición sobre cada fila de entrada suele pertenecer a WHERE, salvo que busques deliberadamente otra semántica de grupos.
Documentación y próximos pasos
Continúa con la lección de GROUP BY y HAVING. Los ejemplos funcionan en SQLite. La distinción entre filas y grupos se aplica a las bases relacionales habituales, pero las reglas de alias y agrupación varían.
Prueba los ejemplos en SQL Lab
Copia la preparación y las consultas de esta guía en el laboratorio. Los ejemplos usan SQLite. Las lecciones y las instrucciones del laboratorio están en inglés.
Abrir SQL Lab