SQL Lab: grups¶
Exercici sql-lab-groups-annex.pdf · PDF
Quants Pokémon de foc hi ha? Ja saps com esbrinar-ho: una consulta amb WHERE type_1 = 'fire', i la resposta és 59. Ara fes-ho amb l'aigua. Després amb l'herba. Després amb els altres quinze tipus.
Divuit consultes per a una pregunta petita. Ningú no ho vol. I les preguntes que de debò es fan sobre les dades gairebé mai no són «ensenya'm les cartes». Són quantes, quant, quina és la mitjana, quin grup guanya. A la lliçó anterior cada resposta era una llista de cartes. En aquesta, la resposta és un nombre sobre moltes cartes, i després el mateix nombre per a cada grup alhora.
Què aprendràs
- Resumir una taula sencera en una sola fila amb les funcions d'agregació
COUNT,SUM,AVG,MINiMAX - Explicar per què
COUNT(*)iCOUNT(column)poden donar nombres diferents sobre la mateixa taula - Dividir una taula en grups amb
GROUP BYi calcular una xifra per grup - Quedar-te només amb alguns grups amb
HAVING - Desactivar la trampa del dia:
WHEREiHAVINGno són el mateix - Llegir l'error
must appear in the GROUP BY clausei arreglar la consulta
Un nombre per a tota la capsa¶
Una funció d'agregació llegeix moltes files i retorna un valor.
Mantenim la capsa de sabates de SQL Lab: filtres: la taula pokemon és una capsa de 949 cartes col·leccionables, una fila és una carta, una columna és una línia impresa a la carta. Una funció d'agregació llegeix un munt sencer de cartes i diu un sol nombre.
| Funció | Amb la capsa de sabates | Sobre pokemon |
|---|---|---|
COUNT(*) |
Quantes cartes | 949 |
MIN(x), MAX(x) |
El valor més petit i el més gran impresos en aquella línia | attack: 5 i 190 |
SUM(x) |
Suma aquella línia sobre totes les cartes | attack: 75419 |
AVG(x) |
La suma dividida pel nombre de cartes: la mitjana | attack: 79.47... |
Obre SQL → SQL Lab (base de dades Dades classe, esquema public) i executa:
SELECT MIN(attack), MAX(attack), SUM(attack), AVG(attack) FROM pokemon;
| min | max | sum | avg |
|---|---|---|---|
| 5 | 190 | 75419 | 79.4720758693361433 |
Hi han entrat 949 cartes. N'ha sortit una sola fila. Aquesta és la marca d'una agregació: per gran que sigui la taula, obtens una sola línia.
La mitjana és correcta i il·legible. Dues eines petites ho arreglen:
SELECT ROUND(AVG(attack), 1) AS avg_attack FROM pokemon;
| avg_attack |
|---|
| 79.5 |
ROUND(value, 1)arrodoneix a un decimal.ROUND(value)arrodoneix a un nombre enter.ASdona a la columna del resultat un nom que tries tu. Aquest nom s'anomena àlies.
Una agregació funciona amb WHERE tal com t'esperes: primer es treuen les cartes, després es compten. SELECT COUNT(*) FROM pokemon WHERE type_1 = 'fire' retorna 59.
COUNT(*) no és COUNT(column)¶
La mateixa taula, tres recomptes:
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 i 510, de la mateixa capsa. Qui menteix? Ningú. Responen preguntes diferents:
| Tu escrius | Compta | Resultat |
|---|---|---|
COUNT(*) |
Cartes | 949 |
COUNT(type_2) |
Cartes on la línia type_2 no és en blanc |
510 |
COUNT(DISTINCT type_1) |
Valors diferents de la línia type_1 |
18 |
I 949 − 510 = 439: els Pokémon d'un sol tipus que vas trobar amb IS NULL a la lliçó anterior.
Compte: les agregacions se salten els blancs
COUNT(column), SUM, AVG, MIN i MAX ignoren tots NULL. Normalment és el que vols: una mitjana de les cartes que tenen valor. Però vol dir que una mitjana es pot calcular sobre menys files de les que penses. Quan un nombre importa, posa COUNT(*) i COUNT(column) un al costat de l'altre i mira quantes cartes han participat de debò.
GROUP BY: fer munts¶
GROUP BYdivideix les files en grups, un grup per cada valor diferent d'una columna, i retorna una fila de resultat per grup.
Agafa una baralla de cartes i reparteix-la en munts, un munt per pal. Quants munts? Un per cada pal diferent. Ara compta cada munt. Quants nombres obtens? Un per munt.
Això és tot el que fa GROUP BY: un munt per cada valor diferent, una fila de resultat per munt.
SELECT type_1, COUNT(*) AS n
FROM pokemon
GROUP BY type_1
ORDER BY n DESC;
18 files. Les tres primeres i l'última:
| type_1 | n |
|---|---|
| water | 126 |
| normal | 111 |
| grass | 84 |
| ... | ... |
| flying | 4 |
Les divuit consultes de dalt de tot de la pàgina, en quatre línies. Recordes SELECT DISTINCT type_1? També retornava 18 files. Els mateixos munts; ara, a més, cada munt es compta.
Pots demanar diverses xifres de cada munt alhora:
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 capsa</b><br>949 cartes"] --> B["<b>GROUP BY type_1</b><br>reparteix-les en munts"]
B --> P1["water<br>126 cartes"]
B --> P2["normal<br>111 cartes"]
B --> P3["...16 munts més"]
P1 --> R["<b>Resultat</b><br>18 files:<br>una per munt"]
P2 --> R
P3 --> R
Compte: GROUP BY no ordena
Els munts surten en l'ordre que a la base de dades li va bé. Si vols el munt més gran primer, digues-ho amb ORDER BY. I els blancs no es llencen: les files amb un NULL a la columna d'agrupació formen el seu propi munt. Aquest munt el trobaràs a «Prova-ho».
L'error que es troba tothom¶
Afegeix el nom del Pokémon a la consulta de dalt:
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
Llegeix-lo amb els munts al davant. El munt d'aigua té 126 cartes. Quin un nom ha d'imprimir la base de dades per a tot el munt? No pot triar-ne, així que es nega.
La regla: en una consulta agrupada, cada columna de SELECT és o bé allò pel qual has fet els munts, o bé és dins d'una funció d'agregació.
HAVING: llença munts sencers¶
HAVINGconserva només els grups per als quals una condició és certa. És un filtre per a munts, no per a cartes.
Mira un altre cop l'última fila del primer resultat: flying, 4 cartes. Una mitjana de quatre cartes no val gaire. Queda't només amb els munts grans:
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 |
Queden vuit munts. dragon, el guanyador fa un moment, ha desaparegut: només té 39 cartes.
Compte: no hi ha àlies a HAVING
És temptador escriure HAVING n >= 50, fent servir l'àlies de SELECT. PostgreSQL ho rebutja: en aquest punt encara no coneix n. Repeteix la funció: HAVING COUNT(*) >= 50. A ORDER BY l'àlies sí que funciona, com has vist més amunt.
WHERE o HAVING? Cartes o munts¶
Aquesta és la idea que la gent confon més. Les dues línies filtren. Filtren coses diferents, en moments diferents.
WHERE |
HAVING |
|
|---|---|---|
| Filtra | Cartes (files) | Munts (grups) |
| Quan | Abans de fer els munts | Després de comptar els munts |
| La condició parla de | Una carta: type_2 IS NULL, attack > 100 |
Un munt sencer: COUNT(*) >= 50, AVG(speed) > 80 |
| Pot fer servir una agregació? | No | Sí |
Per això falla això:
SELECT type_1, COUNT(*) FROM pokemon WHERE COUNT(*) >= 50 GROUP BY type_1;
ERROR: aggregate functions are not allowed in WHERE
Quan s'executa WHERE, encara no hi ha munts. No hi ha res per comptar.
Una consulta pot fer servir totes dues. Atac mitjà per tipus, només tipus amb almenys 50 Pokémon, ignorant les evolucions 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 és una Mega» és una pregunta sobre cada carta: WHERE. «Té almenys 50» és una pregunta sobre cada munt: HAVING. Compara-ho amb la taula anterior: rock ha baixat de 90 a 87 i fire de 84 a 81. Les evolucions Mega són molt fortes, així que feien pujar les mitjanes.
L'ordre real d'una consulta¶
Ara ja coneixes set línies. Les escrius en un ordre. La base de dades les executa en un altre, i aquest ordre explica totes les regles d'aquesta pàgina.
| Tu escrius | S'executa | Amb la capsa de sabates |
|---|---|---|
SELECT |
5è | Llegeix les línies que has demanat i calcula les agregacions |
FROM |
1r | Obre la capsa |
WHERE |
2n | Treu cartes, una per una |
GROUP BY |
3r | Fes munts amb les cartes que queden |
HAVING |
4t | Llença munts sencers |
ORDER BY |
6è | Ordena les files del resultat |
LIMIT |
7è | Guarda les primeres |
flowchart LR
F["<b>1 · FROM</b><br>obre la capsa"] --> W["<b>2 · WHERE</b><br>treu cartes"]
W --> G["<b>3 · GROUP BY</b><br>fes munts"]
G --> H["<b>4 · HAVING</b><br>llença munts sencers"]
H --> S["<b>5 · SELECT</b><br>llegeix i calcula"]
S --> O["<b>6 · ORDER BY</b><br>ordena"]
O --> L["<b>7 · LIMIT</b><br>guarda el de dalt"]
WHERE s'executa abans que existeixin els munts, així que no els pot comptar. HAVING s'executa abans que SELECT, així que no coneix els teus àlies. ORDER BY s'executa després de SELECT, així que sí que els coneix.
Prova-ho: quin tipus guanya?¶
Una consulta per pregunta, a SQL Lab. Escriu la teva primer i després compara.
| N. | Pregunta |
|---|---|
| 1 | hp mínim, màxim i mitjà de tota la taula (la mitjana amb un decimal), en una sola consulta. |
| 2 | Velocitat mitjana per primer tipus, primer el més ràpid. Quins són els tres primers? |
| 3 | El mateix, però només tipus amb almenys 30 Pokémon i una velocitat mitjana superior a 80. Qui queda fora? |
| 4 | Quants Pokémon hi ha per generation_id? Quantes files surten, i què té d'estrany una d'elles? |
| 5 | Per a cada primer tipus: quants Pokémon hi ha, i quants d'ells tenen un segon tipus. Una sola consulta. |
Solucions
1 — SELECT MIN(hp), MAX(hp), ROUND(AVG(hp), 1) FROM pokemon; → 1, 255 i 69.0 (la graella de SQL Lab ho mostra com a 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 — Afegeix HAVING COUNT(*) >= 30 AND AVG(speed) > 80 després de la línia GROUP BY → electric 87, dragon 83, psychic 81. flying queda fora: era el primer, però amb només 4 cartes.
4 — SELECT generation_id, COUNT(*) AS n FROM pokemon GROUP BY generation_id ORDER BY generation_id; → 8 files, no 7: 151, 100, 135, 107, 156, 72 i 81 per a les generacions 1 a 7, i un munt amb el generation_id en blanc i 147 cartes.
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 i 60, normal 111 i 44, grass 84 i 45...
La pregunta 4 deixa dos fils per lligar. Qui són les 147 cartes sense generació? I què és la generació 5: quina regió, quin any? La taula té un número, però no què significa.
Idees clau¶
- Una funció d'agregació (
COUNT,SUM,AVG,MIN,MAX) converteix moltes files en un valor.ROUNDl'endreça,ASel nomena amb un àlies. COUNT(*)compta files;COUNT(column)compta les files on aquella columna no és en blanc. Les agregacions se saltenNULL.GROUP BYfa un munt per cada valor diferent i retorna una fila per munt. No ordena, i els blancs formen el seu propi munt.- En una consulta agrupada, cada columna de
SELECTés una clau de munt o és dins d'una agregació. WHEREfiltra cartes abans de fer els munts;HAVINGfiltra munts després de comptar-los.- La base de dades executa
FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT.
Comprovació ràpida¶
1. Quantes files retorna SELECT MAX(attack), AVG(attack) FROM pokemon;?
- 949, una per Pokémon
- 2, una per funció
- 1
- 18, una per tipus
Una funció d'agregació llegeix moltes files i retorna un valor. Sense GROUP BY, la taula sencera és un sol munt, així que el resultat és una fila, aquí amb dues columnes.
2. A pokemon, COUNT(*) retorna 949 i COUNT(type_2) retorna 510. Per què?
-
COUNT(type_2)compta els valors diferents detype_2 -
COUNT(type_2)només compta les files ontype_2no és en blanc -
COUNT(*)compta algunes files dues vegades - 439 files tenen un error a
type_2
COUNT(*) compta cartes. COUNT(column) compta cartes on aquella línia no és NULL. Els 439 que falten són els Pokémon d'un sol tipus. Els valors diferents serien COUNT(DISTINCT type_2).
3. SELECT type_1, COUNT(*) FROM pokemon GROUP BY type_1; retorna 18 files. Què representa cada fila?
- Un Pokémon
- Un munt: tots els Pokémon que comparteixen el mateix primer tipus
- Una columna de la taula
- Una generació
GROUP BY fa un munt per cada valor diferent de la columna i retorna una fila per munt. Hi ha 18 primers tipus diferents, així que hi ha 18 munts i 18 files.
4. Per què SELECT type_1, pokemon, COUNT(*) FROM pokemon GROUP BY type_1; dona error?
- Perquè
COUNT(*)necessita un nom de columna a dins - Perquè una consulta no pot seleccionar més de dues columnes
- Perquè cada munt té molts noms, i la base de dades no en pot triar un per imprimir
- Perquè
pokemontambé és el nom de la taula
El munt d'aigua té 126 cartes amb 126 noms, però el resultat té una fila per munt. En una consulta agrupada, cada columna de SELECT ha de ser una clau de munt (type_1) o anar dins d'una agregació.
5. Vols els tipus amb més de 50 Pokémon. On va la condició COUNT(*) > 50?
- A
WHERE, abans deGROUP BY - A
HAVING, després deGROUP BY - A
ORDER BY - A
SELECT, al costat dels noms de columna
La condició parla d'un munt sencer, així que va a HAVING. WHERE s'executa abans que existeixin els munts: posa-la allà i PostgreSQL respon aggregate functions are not allowed in WHERE.
6. En quin ordre executa realment la base de dades aquestes línies?
-
SELECT→FROM→WHERE→GROUP BY→HAVING -
FROM→GROUP BY→WHERE→HAVING→SELECT -
FROM→WHERE→GROUP BY→HAVING→SELECT -
FROM→SELECT→WHERE→HAVING→GROUP BY
Obre la capsa (FROM), treu cartes (WHERE), fes munts (GROUP BY), llença munts sencers (HAVING), i només llavors llegeix les línies i calcula (SELECT). Escrius SELECT primer, però s'executa cinquè.
7. Una taula sales(shop, product, amount) té una fila per venda. Vols l'import total per botiga, comptant només les vendes de més de 10 euros, i només per a les botigues amb més de 100 d'aquestes vendes. Tria les dues afirmacions que són certes.
-
amount > 10va aWHERE, perquè és una pregunta sobre cada venda -
COUNT(*) > 100va aHAVING, perquè és una pregunta sobre el munt de cada botiga - Les dues condicions van a
WHERE, unides ambAND -
SELECT shop, product, SUM(amount)funciona ambGROUP BY shop
Cartes o munts? Una venda és una carta: WHERE amount > 10. Una botiga és un munt: HAVING COUNT(*) > 100. L'última opció falla amb must appear in the GROUP BY clause: el munt d'una botiga té molts productes.
La generació 5 té 156 Pokémon, però quina regió és la generació 5? Això està escrit en una altra taula. Tot seguit: SQL Lab: joins, on llegiràs dues taules com si fossin una, i les 147 cartes donen problemes.