GROUP BY agrupa filas en categorías
Las funciones de agregación como COUNT, SUM y AVG reducen muchas filas a un único valor. Con GROUP BY puedes hacer lo mismo, pero por categoría: un valor por cliente, por mes o por estado. Cada valor único (o combinación de valores) se convierte en una sola fila del resultado.
Tres clientes, tres filas de salida. Las seis filas originales desaparecieron: se colapsaron en grupos por cliente, con COUNT(*) y SUM(amount) calculados dentro de cada uno.
La idea mental es esta: GROUP BY customer le dice a SQLite "trata todas las filas con el mismo cliente como un solo grupo". A partir de ahí, las funciones de agregación operan sobre cada grupo por separado.
Qué puedes poner en el SELECT
Aquí es donde mucha gente se traba. Al usar GROUP BY, cada columna que aparezca en el SELECT tiene que estar o bien en la cláusula GROUP BY, o bien dentro de una función de agregación. Si no, el valor es ambiguo: ¿de qué fila del grupo debería salir?
Si escribieras SELECT region, rep, SUM(amount) junto con GROUP BY region, SQLite lo ejecutaría sin quejarse (es permisivo donde otras bases de datos directamente lo rechazan), pero el valor de rep se elegiría de forma arbitraria dentro del grupo. Obtendrías un nombre de rep por región, sin ninguna garantía de cuál. No te confíes de eso: agrupa por todas las columnas no agregadas que muestres.
HAVING: filtrar grupos después de la agregación en SQLite
WHERE filtra filas antes de agrupar. HAVING filtra grupos después de agrupar. Esa es toda la diferencia entre WHERE y HAVING en SQLite, y es justamente la razón por la que no puedes poner COUNT(*) > 1 dentro de un WHERE: en el momento en que se ejecuta WHERE, ese conteo todavía no existe.
Cleo solo hizo un pedido, así que su grupo queda fuera. Ada y Boris se mantienen. La condición se evalúa sobre el valor agregado de cada grupo, no contra las filas individuales.
En SQLite puedes usar directamente en HAVING los alias de columna definidos en el SELECT:
Suele leerse mejor que repetir SUM(amount) en la cláusula HAVING.
Diferencia entre WHERE y HAVING en SQLite: úsalas juntas
No es que tengas que elegir entre una u otra. WHERE filtra qué filas entran en la agrupación; HAVING filtra qué grupos terminan apareciendo en el resultado. En la práctica, casi todas las consultas reales combinan las dos.
Léelo de arriba abajo, en el orden en que se ejecuta:
WHERE status = 'paid'— descarta por completo las filas con reembolso.GROUP BY customer— agrupa lo que queda por cliente.SUM(amount)se calcula para cada grupo.HAVING SUM(amount) > 75— se queda solo con los grupos que superan el umbral.
Sobreviven Boris (80 + 20 = 100) y Cleo (200). El único pedido pagado de Ada fue de 50, así que no llega al mínimo.
Varias condiciones y varias columnas en el GROUP BY
HAVING admite los mismos operadores booleanos que WHERE — AND, OR, NOT — y puedes agrupar por más de una columna para obtener subgrupos:
Cada par (region, quarter) forma un grupo aparte. La cláusula HAVING exige que el total supere 100 y que haya al menos dos operaciones. Solo ('North', 'Q1') y ('South', 'Q2') cumplen ambas condiciones.
Caso práctico: detectar duplicados con HAVING
Una consulta del tipo GROUP BY ... HAVING COUNT(*) > 1 es la forma habitual de encontrar valores duplicados en una columna:
Aparecen dos duplicados. A partir de aquí lo normal es decidir si fusionas las cuentas, añades una restricción UNIQUE o limpias los datos directamente — pero la consulta para detectarlos siempre tiene la misma forma.
HAVING sin GROUP BY en SQLite
Es poco común, pero perfectamente válido. Cuando no hay GROUP BY, SQLite trata todo el resultado como un único grupo, y HAVING lo filtra en bloque: o recibes todos los valores agregados, o no recibes nada:
La fila aparece porque la suma da 160. Si cambias el umbral a > 200, la consulta no devuelve ninguna fila. En la práctica casi siempre vas a usar HAVING junto con GROUP BY, pero está bien saber que el lenguaje no lo exige.
Resumen rápido
GROUP BYagrupa las filas en cubos por clave; las funciones de agregación se calculan dentro de cada cubo.- Toda columna del
SELECTque no sea una agregación debe aparecer también enGROUP BY. WHEREfiltra filas antes de agrupar;HAVINGfiltra grupos después.- Agregaciones como
COUNT(*)ySUM(...)van enHAVING, nunca enWHERE. HAVINGadmite condiciones compuestas y puede usar los alias delSELECT.
Lo que viene: claves foráneas
Agregar datos de una sola tabla está bien, pero la mayoría de los esquemas reales reparten la información en varias tablas: los pedidos por un lado, los clientes por otro, los productos en otra más. Las claves foráneas son las que conectan esas tablas y mantienen la consistencia entre ellas. De eso va el siguiente capítulo.
Preguntas frecuentes
¿Cuál es la diferencia entre WHERE y HAVING en SQLite?
WHERE filtra filas individuales antes de agruparlas. HAVING filtra grupos después de la agregación. Es decir, WHERE amount > 100 se queda solo con las filas mayores a 100, mientras que HAVING SUM(amount) > 100 se queda con los grupos cuya suma supera 100. Las funciones de agregación como COUNT o SUM no se pueden usar en WHERE, y precisamente para eso existe HAVING.
¿Se puede usar HAVING sin GROUP BY en SQLite?
Sí. Si no hay GROUP BY, SQLite trata todo el resultado como un único grupo, y HAVING filtra ese grupo entero como una sola unidad. La consulta devuelve una fila o ninguna. En la práctica casi no se ve, porque cuando aparece HAVING lo normal es que vaya acompañado de un GROUP BY.
¿Cómo filtrar grupos por COUNT en SQLite?
El truco está en poner el agregado en HAVING, no en WHERE. Por ejemplo, SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id HAVING COUNT(*) > 1 devuelve los clientes con más de un pedido. SQLite además te permite usar dentro de HAVING un alias definido en el SELECT.