SQL Lab: grupos¶
Ejercicio sql-lab-groups-annex.pdf · PDF
¿Cuántos Pokémon de fuego hay? Sabes cómo averiguarlo: una consulta con WHERE type_1 = 'fire', y la respuesta es 59. Ahora haz agua. Luego planta. Luego los otros quince tipos.
Dieciocho consultas para una pregunta pequeña. Nadie quiere eso. Y las preguntas que la gente hace de verdad a los datos casi nunca son "enséñame las cartas". Son cuántos, cuánto, cuál es la media, qué grupo gana. En la última lección cada respuesta era una lista de cartas. En esta, la respuesta es un número sobre muchas cartas, y después el mismo número para todos los grupos a la vez.
Qué aprenderás
- Resumir una tabla entera en una fila con las funciones de agregación
COUNT,SUM,AVG,MINyMAX - Explicar por qué
COUNT(*)yCOUNT(columna)pueden dar números distintos en la misma tabla - Dividir una tabla en grupos con
GROUP BYy calcular una cifra por grupo - Quedarte solo con algunos grupos con
HAVING - Desactivar la trampa del día:
WHEREyHAVINGno son lo mismo - Leer el error
must appear in the GROUP BY clausey corregir la consulta
Un número para toda la caja¶
Una función de agregación lee muchas filas y devuelve un valor.
Mantenemos la caja de zapatos de SQL Lab: filtros: la tabla pokemon es una caja con 949 cartas coleccionables, una fila es una carta, una columna es una línea impresa en ella. Una función de agregación lee un montón entero de cartas y dice un número.
| Función | Con la caja de zapatos | En pokemon |
|---|---|---|
COUNT(*) |
Cuántas cartas hay | 949 |
MIN(x), MAX(x) |
El valor más pequeño y el más grande impresos en esa línea | attack: 5 y 190 |
SUM(x) |
Suma esa línea de todas las cartas | attack: 75419 |
AVG(x) |
La suma dividida entre cuántas hay: la media | attack: 79.47... |
Abre SQL → SQL Lab (base de datos Dades classe, esquema public) y ejecuta:
SELECT MIN(attack), MAX(attack), SUM(attack), AVG(attack) FROM pokemon;
| min | max | sum | avg |
|---|---|---|---|
| 5 | 190 | 75419 | 79.4720758693361433 |
Entraron 949 cartas. Salió una fila. Esa es la marca de una agregación: por grande que sea la tabla, obtienes una sola línea.
La media es correcta e ilegible. Dos herramientas pequeñas lo arreglan:
SELECT ROUND(AVG(attack), 1) AS avg_attack FROM pokemon;
| avg_attack |
|---|
| 79.5 |
ROUND(valor, 1)redondea a un decimal.ROUND(valor)redondea a un número entero.ASda a la columna del resultado un nombre que eliges tú. Ese nombre se llama alias.
Una agregación funciona junto con WHERE tal como esperarías: primero se sacan las cartas, después se cuentan. SELECT COUNT(*) FROM pokemon WHERE type_1 = 'fire' devuelve 59.
COUNT(*) no es COUNT(columna)¶
La misma tabla, tres recuentos:
SELECT COUNT(*) AS total,
COUNT(type_2) AS with_second_type,
COUNT(DISTINCT type_1) AS types
FROM pokemon;
| total | with_second_type | types |
|---|---|---|
| 949 | 510 | 18 |
949 y 510, de la misma caja. ¿Quién miente? Nadie. Responden a preguntas distintas:
| Escribes | Cuenta | Resultado |
|---|---|---|
COUNT(*) |
Cartas | 949 |
COUNT(type_2) |
Cartas donde la línea type_2 no está en blanco |
510 |
COUNT(DISTINCT type_1) |
Valores distintos de la línea type_1 |
18 |
Y 949 − 510 = 439: los Pokémon de un solo tipo que encontraste con IS NULL en la lección anterior.
Cuidado: las agregaciones se saltan los blancos
COUNT(columna), SUM, AVG, MIN y MAX ignoran NULL. Normalmente es lo que quieres: una media de las cartas que tienen valor. Pero significa que una media puede calcularse sobre menos filas de las que crees. Cuando un número importa, pon COUNT(*) y COUNT(columna) uno al lado del otro y mira cuántas cartas participaron de verdad.
GROUP BY: haz montones¶
GROUP BYdivide las filas en grupos, un grupo por cada valor distinto de una columna, y devuelve una fila de resultado por grupo.
Coge una baraja y repártela en montones, uno por palo. ¿Cuántos montones? Uno por cada palo distinto. Ahora cuenta cada montón. ¿Cuántos números obtienes? Uno por montón.
Eso es todo lo que hace GROUP BY: un montón por valor distinto, una fila de resultado por montón.
SELECT type_1, COUNT(*) AS n
FROM pokemon
GROUP BY type_1
ORDER BY n DESC;
18 filas. Las tres primeras y la última:
| type_1 | n |
|---|---|
| water | 126 |
| normal | 111 |
| grass | 84 |
| ... | ... |
| flying | 4 |
Las dieciocho consultas del principio de la página, en cuatro líneas. ¿Recuerdas SELECT DISTINCT type_1? También devolvía 18 filas. Los mismos montones; ahora además se cuenta cada montón.
Puedes pedir varias cifras de cada montón a la vez:
SELECT type_1, COUNT(*) AS n, ROUND(AVG(attack)) AS avg_attack
FROM pokemon
GROUP BY type_1
ORDER BY avg_attack DESC;
| type_1 | n | avg_attack |
|---|---|---|
| dragon | 39 | 109 |
| fighting | 31 | 99 |
| ground | 36 | 96 |
| steel | 30 | 93 |
| ... | ... | ... |
flowchart LR
A["<b>La caja</b><br>949 cartas"] --> B["<b>GROUP BY type_1</b><br>repártelas en montones"]
B --> P1["water<br>126 cartas"]
B --> P2["normal<br>111 cartas"]
B --> P3["...16 montones más"]
P1 --> R["<b>Resultado</b><br>18 filas:<br>una por montón"]
P2 --> R
P3 --> R
Cuidado: GROUP BY no ordena
Los montones salen en el orden que a la base de datos le resulte cómodo. Si quieres el montón más grande primero, dilo con ORDER BY. Y los blancos no se tiran: las filas con un NULL en la columna de agrupación forman su propio montón. Lo encontrarás en "Pruébalo".
El error que todo el mundo encuentra¶
Añade el nombre del Pokémon a la consulta anterior:
SELECT type_1, pokemon, COUNT(*) FROM pokemon GROUP BY type_1;
ERROR: column "pokemon.pokemon" must appear in the GROUP BY clause
or be used in an aggregate function
Léelo con los montones delante. El montón de agua tiene 126 cartas. ¿Qué un nombre debería imprimir la base de datos para todo el montón? No puede elegir, así que se niega.
La regla: en una consulta agrupada, cada columna de SELECT es aquello por lo que has hecho los montones, o va dentro de una función de agregación.
HAVING: tira montones enteros¶
HAVINGse queda solo con los grupos para los que una condición es verdadera. Es un filtro para montones, no para cartas.
Mira otra vez la última fila del primer resultado: flying, 4 cartas. Una media de cuatro cartas no vale gran cosa. Quédate solo con los montones grandes:
SELECT type_1, COUNT(*) AS n, ROUND(AVG(attack)) AS avg_attack
FROM pokemon
GROUP BY type_1
HAVING COUNT(*) >= 50
ORDER BY avg_attack DESC;
| type_1 | n | avg_attack |
|---|---|---|
| rock | 65 | 90 |
| fire | 59 | 84 |
| normal | 111 | 76 |
| grass | 84 | 75 |
| water | 126 | 75 |
| psychic | 64 | 73 |
| bug | 79 | 72 |
| electric | 61 | 68 |
Quedan ocho montones. dragon, el ganador hace un momento, ha desaparecido: solo tiene 39 cartas.
Cuidado: nada de alias en HAVING
Es tentador escribir HAVING n >= 50, usando el alias de SELECT. PostgreSQL lo rechaza: en ese punto todavía no conoce n. Repite la función: HAVING COUNT(*) >= 50. En ORDER BY el alias sí funciona, como has visto más arriba.
¿WHERE o HAVING? Cartas o montones¶
Esta es la idea que más se confunde. Las dos líneas filtran. Filtran cosas distintas, en momentos distintos.
WHERE |
HAVING |
|
|---|---|---|
| Filtra | Cartas (filas) | Montones (grupos) |
| Cuándo | Antes de hacer los montones | Después de contar los montones |
| La condición trata de | Una carta: type_2 IS NULL, attack > 100 |
Un montón entero: COUNT(*) >= 50, AVG(speed) > 80 |
| ¿Puede usar una agregación? | No | Sí |
Por eso esto falla:
SELECT type_1, COUNT(*) FROM pokemon WHERE COUNT(*) >= 50 GROUP BY type_1;
ERROR: aggregate functions are not allowed in WHERE
Cuando se ejecuta WHERE, todavía no hay montones. No hay nada que contar.
Una consulta puede usar las dos. Ataque medio por tipo, solo los tipos con al menos 50 Pokémon, ignorando las evoluciones Mega:
SELECT type_1, COUNT(*) AS n, ROUND(AVG(attack)) AS avg_attack
FROM pokemon
WHERE pokemon NOT LIKE '%-mega%'
GROUP BY type_1
HAVING COUNT(*) >= 50
ORDER BY avg_attack DESC;
| type_1 | n | avg_attack |
|---|---|---|
| rock | 62 | 87 |
| fire | 55 | 81 |
| normal | 107 | 75 |
| ... | ... | ... |
"No es una Mega" es una pregunta sobre cada carta: WHERE. "Tiene al menos 50" es una pregunta sobre cada montón: HAVING. Compara con la tabla anterior: rock bajó de 90 a 87 y fire de 84 a 81. Las evoluciones Mega son muy fuertes, así que estaban subiendo las medias.
El orden real de una consulta¶
Ya conoces siete líneas. Las escribes en un orden. La base de datos las ejecuta en otro, y ese orden explica todas las reglas de esta página.
| Lo escribes | Se ejecuta | Con la caja de zapatos |
|---|---|---|
SELECT |
5.º | Lee las líneas que has pedido y calcula las agregaciones |
FROM |
1.º | Abre la caja |
WHERE |
2.º | Saca cartas, una por una |
GROUP BY |
3.º | Haz montones con las cartas que quedan |
HAVING |
4.º | Tira montones enteros |
ORDER BY |
6.º | Ordena las filas del resultado |
LIMIT |
7.º | Quédate con las primeras |
flowchart LR
F["<b>1 · FROM</b><br>abre la caja"] --> W["<b>2 · WHERE</b><br>saca cartas"]
W --> G["<b>3 · GROUP BY</b><br>haz montones"]
G --> H["<b>4 · HAVING</b><br>tira montones enteros"]
H --> S["<b>5 · SELECT</b><br>lee y calcula"]
S --> O["<b>6 · ORDER BY</b><br>ordena"]
O --> L["<b>7 · LIMIT</b><br>quédate con lo de arriba"]
WHERE se ejecuta antes de que existan los montones, así que no puede contarlos. HAVING se ejecuta antes que SELECT, así que no conoce tus alias. ORDER BY se ejecuta después de SELECT, así que sí.
Pruébalo: ¿qué tipo gana?¶
Una consulta por pregunta, en SQL Lab. Escribe primero la tuya y luego compara.
| N. | Pregunta |
|---|---|
| 1 | hp mínimo, máximo y medio de toda la tabla (la media con un decimal), en una sola consulta. |
| 2 | Velocidad media por primer tipo, el más rápido primero. ¿Cuáles son los tres primeros? |
| 3 | Lo mismo, pero solo los tipos con al menos 30 Pokémon y una velocidad media superior a 80. ¿Quién se cae? |
| 4 | ¿Cuántos Pokémon hay por generation_id? ¿Cuántas filas salen y qué tiene de raro una de ellas? |
| 5 | Para cada primer tipo: cuántos Pokémon hay y cuántos de ellos tienen un segundo tipo. Una consulta. |
Soluciones
1 — SELECT MIN(hp), MAX(hp), ROUND(AVG(hp), 1) FROM pokemon; → 1, 255 y 69.0 (la cuadrícula de SQL Lab lo muestra como 69).
2 — SELECT type_1, ROUND(AVG(speed)) AS avg_speed FROM pokemon GROUP BY type_1 ORDER BY avg_speed DESC; → flying 103, electric 87, dragon 83.
3 — Añade HAVING COUNT(*) >= 30 AND AVG(speed) > 80 después de la línea GROUP BY → electric 87, dragon 83, psychic 81. flying se cae: era el primero, pero con solo 4 cartas.
4 — SELECT generation_id, COUNT(*) AS n FROM pokemon GROUP BY generation_id ORDER BY generation_id; → 8 filas, no 7: 151, 100, 135, 107, 156, 72 y 81 para las generaciones 1 a 7, y un montón con el generation_id en blanco y 147 cartas.
5 — SELECT type_1, COUNT(*) AS n, COUNT(type_2) AS with_second FROM pokemon GROUP BY type_1 ORDER BY n DESC; → water 126 y 60, normal 111 y 44, grass 84 y 45...
La pregunta 4 deja dos cabos sueltos. ¿Quiénes son las 147 cartas sin generación? ¿Y qué es la generación 5: qué región, qué año? La tabla tiene un número, pero no lo que significa.
Ideas clave¶
- Una función de agregación (
COUNT,SUM,AVG,MIN,MAX) convierte muchas filas en un valor.ROUNDlo ordena,ASle pone nombre con un alias. COUNT(*)cuenta filas;COUNT(columna)cuenta las filas donde esa columna no está en blanco. Las agregaciones se saltanNULL.GROUP BYhace un montón por cada valor distinto y devuelve una fila por montón. No ordena, y los blancos forman su propio montón.- En una consulta agrupada, cada columna de
SELECTes una clave de montón o está dentro de una agregación. WHEREfiltra cartas antes de hacer los montones;HAVINGfiltra montones después de contarlos.- La base de datos ejecuta
FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT.
Comprobación rápida¶
1. ¿Cuántas filas devuelve SELECT MAX(attack), AVG(attack) FROM pokemon;?
- 949, una por Pokémon
- 2, una por función
- 1
- 18, una por tipo
Una función de agregación lee muchas filas y devuelve un valor. Sin GROUP BY, la tabla entera es un único montón, así que el resultado es una fila, aquí con dos columnas.
2. En pokemon, COUNT(*) devuelve 949 y COUNT(type_2) devuelve 510. ¿Por qué?
-
COUNT(type_2)cuenta los valores distintos detype_2 -
COUNT(type_2)solo cuenta las filas dondetype_2no está en blanco -
COUNT(*)cuenta algunas filas dos veces - 439 filas tienen un error en
type_2
COUNT(*) cuenta cartas. COUNT(columna) cuenta las cartas donde esa línea no es NULL. Las 439 que faltan son los Pokémon de un solo tipo. Los valores distintos serían COUNT(DISTINCT type_2).
3. SELECT type_1, COUNT(*) FROM pokemon GROUP BY type_1; devuelve 18 filas. ¿Qué representa cada fila?
- Un Pokémon
- Un montón: todos los Pokémon que comparten el mismo primer tipo
- Una columna de la tabla
- Una generación
GROUP BY hace un montón por cada valor distinto de la columna y devuelve una fila por montón. Hay 18 primeros tipos distintos, así que hay 18 montones y 18 filas.
4. ¿Por qué SELECT type_1, pokemon, COUNT(*) FROM pokemon GROUP BY type_1; da un error?
- Porque
COUNT(*)necesita un nombre de columna dentro - Porque una consulta no puede seleccionar más de dos columnas
- Porque cada montón tiene muchos nombres, y la base de datos no puede elegir uno para imprimir
- Porque
pokemontambién es el nombre de la tabla
El montón de agua contiene 126 cartas con 126 nombres, pero el resultado tiene una fila por montón. En una consulta agrupada cada columna de SELECT debe ser una clave de montón (type_1) o ir dentro de una agregación.
5. Quieres los tipos con más de 50 Pokémon. ¿Dónde va la condición COUNT(*) > 50?
- En
WHERE, antes deGROUP BY - En
HAVING, después deGROUP BY - En
ORDER BY - En
SELECT, junto a los nombres de las columnas
La condición trata de un montón entero, así que va en HAVING. WHERE se ejecuta antes de que existan los montones: ponla ahí y PostgreSQL responde aggregate functions are not allowed in WHERE.
6. ¿En qué orden ejecuta realmente la base de datos estas líneas?
-
SELECT→FROM→WHERE→GROUP BY→HAVING -
FROM→GROUP BY→WHERE→HAVING→SELECT -
FROM→WHERE→GROUP BY→HAVING→SELECT -
FROM→SELECT→WHERE→HAVING→GROUP BY
Abre la caja (FROM), saca cartas (WHERE), haz montones (GROUP BY), tira montones enteros (HAVING), y solo entonces lee las líneas y calcula (SELECT). Escribes SELECT primero, pero se ejecuta el quinto.
7. Una tabla sales(shop, product, amount) tiene una fila por venta. Quieres el importe total por tienda, contando solo las ventas de más de 10 euros, y solo de las tiendas con más de 100 de esas ventas. Elige las dos afirmaciones verdaderas.
-
amount > 10va enWHERE, porque es una pregunta sobre cada venta -
COUNT(*) > 100va enHAVING, porque es una pregunta sobre el montón de cada tienda - Las dos condiciones van en
WHERE, unidas conAND -
SELECT shop, product, SUM(amount)funciona conGROUP BY shop
¿Cartas o montones? Una venta es una carta: WHERE amount > 10. Una tienda es un montón: HAVING COUNT(*) > 100. La última opción falla con must appear in the GROUP BY clause: el montón de una tienda tiene muchos productos.
La generación 5 tiene 156 Pokémon, pero ¿qué región es la generación 5? Eso está escrito en otra tabla. Siguiente: SQL Lab: joins, donde lees dos tablas como una, y las 147 cartas dan problemas.