La idea
Una función de agregado toma muchas filas y devuelve un valor: un conteo, una suma, un promedio, un
mínimo o un máximo. SELECT COUNT(*) FROM orders convierte doce mil filas en una sola fila que dice
12000.
GROUP BY hace lo mismo una vez por grupo en lugar de una vez para toda la tabla. Reparte las filas en
montones según el valor de una columna, como channel, y luego cada montón se pliega en una sola fila.
Entran ocho pedidos y salen tres filas. El detalle desaparece a propósito, para que puedas ver el patrón.
Figura 1
GROUP BY channel pliega ocho pedidos en tres filas
| order_id | channel | status |
|---|---|---|
| 1003 | marketplace | completed |
| 1004 | marketplace | completed |
| 1005 | web | completed |
| 1006 | marketplace | cancelled |
| 1007 | marketplace | completed |
| 1008 | web | completed |
| 1009 | app | completed |
| 1010 | web | completed |
| channel | orders |
|---|---|
| marketplace | 4 |
| web | 3 |
| app | 1 |
- Ocho pedidos, una fila cada uno. La pregunta: ¿cuántos pedidos hay por canal? Eso es
SELECT channel, COUNT(*) FROM orders GROUP BY channel. - Primero
GROUP BYreparte las filas en montones, uno por cada canal distinto. Los cuatro pedidos de marketplace van al primer montón. - Luego el agregado corre una vez por montón. Cada montón se convierte en una fila de resultado. Los tres pedidos web se pliegan en una fila que dice 3.
- El montón de app tiene un solo pedido, así que se pliega en una fila que dice 1. Entran ocho filas y salen tres. Los pedidos individuales desaparecen: solo quedan el grupo y su conteo.
Por qué existe
Nadie en una reunión quiere una lista de 12,000 pedidos. Quieren saber cuántos hay por canal, los
ingresos por mes, el pedido promedio por cliente. Son resúmenes, y la idea es perder el detalle de forma
controlada. Sin GROUP BY tendrías que filtrar y contar a mano, una vez por canal, y escribir una
consulta nueva cada vez que apareciera un canal. Con él, una sola consulta atiende todos los grupos,
incluso los que no sabías que existían. El costo es que un resumen puede esconder cosas: un promedio de
40 dólares puede venir de cuarenta pedidos de 40 o de uno de 1,000 y muchos diminutos. Mira siempre un
conteo junto a cada promedio, para saber cuántas filas lo respaldan.
Cómo funciona por dentro
La consulta se ejecuta en un orden fijo, y ese orden explica todas las reglas de este capítulo:
- FROM elige la tabla.
- WHERE conserva o descarta filas individuales.
- GROUP BY reparte las filas que sobrevivieron en montones según la clave.
- Los agregados (
COUNT,SUM,AVG,MIN,MAX) calculan un valor por montón. - HAVING conserva o descarta montones completos, usando los valores de los agregados.
- SELECT, ORDER BY y LIMIT dan forma y cortan lo que queda.
WHERE y HAVING
Los dos filtran, pero en momentos distintos y sobre cosas distintas. WHERE habla de una fila (un
status, un channel). HAVING habla de un grupo (su COUNT(*), su SUM). Una condición sobre un
total no puede ir en WHERE, porque los totales todavía no existen cuando WHERE corre.
Figura 2
WHERE quita filas antes de que existan los grupos, HAVING quita grupos después
-
FROM orders8 pedidos -
WHERE status = 'completed'quita el pedido 1006, una fila -
GROUP BY channelmarketplace 3, web 3, app 1 -
HAVING COUNT(*) >= 3quita app, un grupo -
SELECT channel, COUNT(*)marketplace 3, web 3
- Entran los ocho pedidos. Todavía no se ha agrupado nada, así que no hay ningún conteo que probar.
WHEREmira filas sueltas. El pedido cancelado 1006 falla y sale, así que marketplace solo contará 3 pedidos, no 4.- Ahora las filas se pliegan en grupos: marketplace 3, web 3 y app 1. Solo aquí existen los conteos.
HAVINGmira grupos completos, usando el agregado. El grupo app tiene 1 pedido, así que todo el grupo se descarta.SELECTmuestra lo que queda: dos grupos, dos filas.WHERE COUNT(*) >= 3fallaría, porque en el paso deWHEREtodavía no existe ningún conteo.
Una prueba útil: si la condición menciona un agregado, va en HAVING. Si describe una sola fila, ponla
en WHERE. Las condiciones de fila en WHERE además son más rápidas, porque reducen los datos antes de
que empiece el trabajo de agrupar.
Qué puede llevar SELECT
Después de agrupar hay una fila por grupo, así que cada columna de SELECT debe ser una de dos cosas: la
propia clave de agrupación o un agregado. SELECT channel, status, COUNT(*) ... GROUP BY channel es
ambiguo: un canal tiene varios estados, y la base de datos no sabe cuál mostrarte. O agregas status al
GROUP BY, lo que crea grupos más pequeños, o lo quitas.
Contar: tres versiones
COUNT(*) cuenta filas. COUNT(columna) cuenta las filas donde esa columna no es NULL.
COUNT(DISTINCT columna) cuenta los valores distintos que no son NULL. Todos los demás agregados también
se saltan los NULL: AVG(unit_cost) promedia solo los productos que tienen costo.
Figura 3
COUNT(*) cuenta filas, COUNT(columna) cuenta valores que no son NULL
| customer_id | first_name | referral_source |
|---|---|---|
| 1 | Owen | search |
| 2 | Douglas | NULL |
| 3 | Henry | NULL |
| 4 | Brian | |
| 5 | Kyle | NULL |
| count_star | count_referral |
|---|---|
| 5 | 2 |
- Cinco clientes. Tres no tienen
referral_source, que esNULL. COUNT(*)cuenta filas. No mira dentro de ellas, así que la respuesta es 5.COUNT(referral_source)cuenta solo las filas donde esa columna tiene un valor. Los tres NULL se saltan y la respuesta es 2.
Por eso "¿cuántos clientes llegaron por referencia?" y "¿cuántos clientes hay?" son preguntas distintas con conteos distintos, y por eso debes decir cuál conteo quieres.
Revisar un resultado agrupado
Suma los conteos de los grupos: 4 + 3 + 1 es 8, igual que las ocho filas con que empezaste. Si los
números de un GROUP BY no suman el COUNT(*) simple de la tabla filtrada, algo se perdió: normalmente
una clave NULL. Las filas con NULL en la columna de agrupación no se descartan. Forman un grupo
propio, que se muestra con la clave vacía o NULL, así que búscalo en la salida antes de fiarte de los
totales.
Razones y tasas
Muchos números de negocio son un agregado dividido por otro: reembolsos entre pedidos, pedidos
completados entre todos los pedidos. Calcula ambos en la misma consulta y divídelos, recordando que en
algunas bases de datos la división entera puede redondear a cero. Multiplica por 1.0 primero, o
convierte uno de los lados a decimal. Lo habitual es calcular la tasa por grupo: el grupo es el canal, y
los dos números se pliegan del mismo montón.
Ingresos a partir de las líneas de pedido
Los ingresos no se guardan por pedido. Son la suma de cantidad por precio sobre las líneas de cada pedido,
así que una consulta de ingresos suma una expresión: SUM(quantity * unit_price). Los agregados aceptan
expresiones, no solo nombres de columna. Comprueba que sumas en el nivel correcto. Si primero haces un
join y luego sumas una columna que pertenece al pedido, cada línea la repite.
Errores comunes
"COUNT(columna) y COUNT(*) son lo mismo"
Solo coinciden cuando la columna no tiene NULL. En Harbor & Pine, COUNT(referral_source) es cerca de
dos tercios de COUNT(*) en los clientes, porque muchos clientes no tienen fuente de referencia. Elige
a propósito, y para "cuántas filas" usa COUNT(*).
"WHERE y HAVING son intercambiables"
HAVING con una condición de fila a veces funciona, pero filtra después de que se hizo el trabajo de
agrupar y es fácil de malinterpretar. Y un total en WHERE es un error. Piensa en cuándo corre cada uno
y la elección se hace sola. Poner una condición de fila en el lugar equivocado puede cambiar en silencio
lo que se cuenta.
"GROUP BY ordena el resultado"
Reparte las filas en montones internamente, pero no promete el orden de los grupos en la salida. Si
quieres el canal más grande primero, agrega ORDER BY COUNT(*) DESC. Sin eso, la misma consulta puede
listar los canales en otro orden mañana.
Ponte a prueba
Una pregunta. Al responderla, la lección queda marcada en el mapa; nunca bloquea un nivel.
No del todo. WHERE corre primero. Cuando GROUP BY arma los grupos, el pedido 1006 ya no está, así que el conteo es 3.Prueba otra respuesta.
Correcto. WHERE descarta el 1006 antes de agrupar, así que marketplace es un grupo de 3. Entonces HAVING COUNT(*) >= 4 rechaza el grupo.
No del todo. HAVING prueba la condición que escribiste contra cada grupo. Un conteo de 3 no cumple >= 4, así que el grupo se elimina.Prueba otra respuesta.
No del todo. Se usan juntos todo el tiempo: WHERE filtra filas antes de agrupar y HAVING filtra grupos después.Prueba otra respuesta.
Lección hecha: ya está marcada en el mapa.