Salta el contingut

SQL Lab: filtres

Exercici sql-lab-filters-annex.pdf · PDF

Algú t'allarga una capsa de sabates amb 949 cartes col·leccionables de Pokémon i et demana: quin és el més ràpid de tipus elèctric? No llegeixes en veu alta les 949 cartes. Treus les elèctriques, les ordenes per velocitat, mires la carta de dalt de tot i en llegeixes el nom.

Quatre moviments amb les mans. En una base de dades aquests quatre moviments tenen nom, i junts formen una frase que la base de dades entén. A la lliçó anterior vas connectar Superset a la base de dades de classe i vas pujar la taula pokemon. Ara aprens a demanar-li menys: menys columnes, menys files, en un ordre útil. En el joc de crafting d'aquest mòdul encara ets a l'etapa de farm i loot, però un bon jugador no es duu a casa tot el mapa, només el que necessita la recepta.

Què aprendràs

  • Escriure una consulta que retorni només les columnes que necessites, en lloc de SELECT *
  • Quedar-te només amb les files que necessites amb WHERE, fent servir operadors de comparació i AND / OR
  • Ordenar un resultat amb ORDER BY i retallar-lo amb LIMIT per respondre preguntes de «top N»
  • Llistar els valors diferents d'una columna amb DISTINCT
  • Triar el filtre especial adequat: LIKE, IN, BETWEEN o IS NULL
  • Desactivar la trampa del dia: per què = NULL no troba res, i per què una consulta equivocada gairebé mai dona error

Una capsa de sabates amb 949 cartes

Una consulta és una pregunta escrita en un llenguatge que la base de dades entén. Aquest llenguatge és SQL (Structured Query Language).

Una sola analogia per a tota la pàgina: la taula pokemon és una capsa de sabates plena de cartes col·leccionables.

A la base de dades A la capsa de sabates
La taula pokemon La capsa
Una fila Una carta: un Pokémon
Una columna Una línia impresa a totes les cartes: nom, tipus, atac...
Una consulta El que fas amb les mans per respondre una pregunta

La capsa conté 949 cartes, i cada carta té 22 línies impreses. En aquesta lliçó en fem servir vuit:

Columna Què conté Exemple (charizard)
pokemon El nom, en minúscules charizard
type_1 Primer tipus fire
type_2 Segon tipus, en blanc si no n'hi ha flying
hp, attack, speed Estadístiques base, nombres enters 78, 84, 100
weight Pes en quilograms 90.5
generation_id Número de generació, de l'1 al 7, en blanc en alguns 1

Obre SQL → SQL Lab a Superset, tria la base de dades Dades classe i l'esquema public, i ja pots escriure. Executes una consulta amb el botó Run o amb Ctrl+Enter.

Per què SQL sembla anglès

El 1974, Donald Chamberlin i Raymond Boyce, dos investigadors d'IBM a San Jose (Califòrnia), van publicar un llenguatge per fer preguntes a les bases de dades. Volien que les persones que no eren programadores el poguessin llegir, així que el van anomenar SEQUEL: Structured English Query Language. El nom ja era una marca registrada d'una empresa d'aviació, així que va perdre les vocals i es va quedar en SQL. Més de cinquanta anys després, encara escrius SELECT, FROM i WHERE exactament com ells.

La forma d'una consulta

Una consulta té fins a cinc línies, sempre en aquest ordre. Cadascuna és un moviment de les mans.

Línia Amb la capsa de sabates Regla
SELECT Quines línies de la carta llegeixes Noms de columna, separats per comes. * vol dir totes.
FROM Quina capsa La taula.
WHERE Quines cartes treus Una condició que és certa o falsa per a cada fila.
ORDER BY Com ordenes el munt ASC (primer els petits, per defecte) o DESC (primer els grans).
LIMIT Quantes cartes guardes de dalt Un número. Sempre l'última.
flowchart LR
    A["<b>FROM</b><br>obre la capsa:<br>949 cartes"] --> B["<b>WHERE</b><br>treu les cartes<br>que vols"]
    B --> C["<b>ORDER BY</b><br>ordena el munt"]
    C --> D["<b>LIMIT</b><br>guarda les de dalt"]
    D --> E["<b>SELECT</b><br>llegeix només les línies<br>que has demanat"]

Aquí tens la pregunta de dalt de tot de la pàgina, escrita com a consulta:

SELECT pokemon, speed
FROM pokemon
WHERE type_1 = 'electric'
ORDER BY speed DESC
LIMIT 1;
pokemon speed
electrode 150

Una fila, dues columnes. La resposta, i res més. Les seccions següents construeixen aquesta frase línia per línia.

SELECT: llegeix només les línies que necessites

SELECT tria les columnes del resultat. FROM indica la taula de la qual surten.

SELECT * FROM pokemon et dona les 22 columnes de les 949 files. Va bé per fer una primera ullada, i no serveix com a resposta. Posa-hi el nom de les columnes:

SELECT pokemon, type_1, attack FROM pokemon;

Continua retornant 949 files, però només tres columnes. Les quatre primeres:

pokemon type_1 attack
bulbasaur grass 49
ivysaur grass 62
venusaur grass 82
charmander fire 52

Compte: una coma que falta no dona error

Escriu SELECT pokemon type_1 FROM pokemon (sense coma) i la consulta s'executa. Però obtens una columna, anomenada type_1, plena de noms. Sense la coma, SQL llegeix la segona paraula com un nou nom per a la primera columna. Si et falta una columna que has demanat, compta les comes.

WHERE: treu cartes de la capsa

WHERE conserva només les files per a les quals una condició és certa. Una condició és una pregunta de sí o no que es fa a cada fila, una per una.

SELECT pokemon, type_1, attack FROM pokemon WHERE type_1 = 'fire';

Això retorna 59 files: charmander, charmeleon, charizard, vulpix... La condició type_1 = 'fire' s'ha preguntat 949 vegades, i 59 cartes han dit que sí.

El = és un operador de comparació. N'hi ha sis:

Operador Significat Exemple Files
= igual a type_1 = 'fire' 59
<> diferent de type_1 <> 'fire' 890
> més gran que attack > 150 19
>= més gran o igual que attack >= 150 30
< més petit que speed < 20 17
<= més petit o igual que speed <= 20 33

Dues regles causen la majoria dels errors la primera setmana d'SQL:

Regla Correcte Incorrecte Què passa
El text va entre cometes simples, els nombres no type_1 = 'fire', attack > 150 type_1 = fire Un error: la base de dades pensa que fire és una columna. SQL Lab diu We can't seem to resolve the column "fire".
El text distingeix majúscules i minúscules 'fire' 'Fire' Cap error, i 0 files.

Compte: zero files no sempre vol dir «no n'hi ha»

WHERE type_1 = 'Fire' no retorna res perquè cap carta diu Fire amb F majúscula. La base de dades no s'ha queixat: ha respost la pregunta que has fet. Abans de creure't un resultat buit, revisa l'ortografia. I les cometes dobles no són una sortida: a PostgreSQL "fire" vol dir nom de columna, 'fire' vol dir text.

Dues condicions: AND, OR

Un operador lògic uneix dues condicions. AND: les dues han de ser certes. OR: n'hi ha prou amb una.

SELECT pokemon, type_1, type_2 FROM pokemon
WHERE type_1 = 'dragon' AND type_2 = 'flying';

Sis files: dragonite, altaria, salamence, rayquaza i les formes Mega dels dos últims. Canvia AND per OR i pregunta alguna cosa més àmplia:

SELECT pokemon, type_1, type_2 FROM pokemon
WHERE type_1 = 'dragon' OR type_2 = 'dragon';

61 files: tots els Pokémon que són de tipus drac en qualsevol de les dues ranures. AND fa un resultat més petit, OR el fa més gran.

ORDER BY i LIMIT: ordena el munt, guarda el de dalt

ORDER BY ordena el resultat per una columna. LIMIT conserva només les primeres files.

SELECT pokemon, type_1, attack FROM pokemon
WHERE attack > 150
ORDER BY attack DESC;

19 files, primer els més forts. Juntes, aquestes dues línies responen qualsevol pregunta de «top N»:

SELECT pokemon, attack FROM pokemon ORDER BY attack DESC LIMIT 5;
pokemon attack
mewtwo-mega-x 190
heracross-mega 185
kartana 181
deoxys-attack 180
groudon-primal 180

Mira les dues últimes files. Tres Pokémon tenen un atac de 180: deoxys-attack, groudon-primal i rayquaza-mega. Només n'hi caben dos en un top 5, i la base de dades ha triat com ha volgut. La teva pantalla pot mostrar un altre parell, i tots dos tindríeu raó.

Compte: LIMIT talla els empats arbitràriament

Quan diverses files comparteixen el valor pel qual ordenes, el seu ordre no està definit. Per decidir-lo tu, afegeix una segona columna d'ordenació: ORDER BY attack DESC, pokemon ordena els empats pel nom. I recorda que LIMIT 5 tot sol, sense ORDER BY, et dona unes cinc files qualssevol, no les cinc millors.

DISTINCT: conèixer una columna

DISTINCT elimina les files repetides del resultat, de manera que cada valor diferent apareix una sola vegada.

Abans de filtrar per una columna, esbrina quins valors pot tenir:

SELECT DISTINCT type_1 FROM pokemon ORDER BY type_1;

18 files, de bug a water: els 18 tipus, cadascun una vegada. Ara saps que el tipus s'escriu fire, no Fire ni FIRE. DISTINCT és el remei de la trampa de zero files de dalt.

Quatre filtres especials

Els operadors de comparació cobreixen els nombres i el text exacte. Quatre filtres més cobreixen la resta.

Filtre Amb la capsa de sabates Exemple Files
LIKE Cartes amb un text que s'assembla a un patró pokemon LIKE 'pika%' 13
IN Cartes amb un valor que és a la meva llista type_1 IN ('fire', 'water', 'grass') 269
BETWEEN Cartes dins d'un interval, amb els dos extrems inclosos speed BETWEEN 100 AND 120 126
IS NULL Cartes on aquella línia és en blanc type_2 IS NULL 439

LIKE fa servir dos comodins, símbols que representen text que no coneixes: % és qualsevol text (també cap), _ és exactament un caràcter. 'pika%' vol dir «comença per pika»: pikachu i les seves dotze disfresses (pikachu-rock-star, pikachu-phd...).

IN és una forma abreujada de diversos OR: type_1 = 'fire' OR type_1 = 'water' OR type_1 = 'grass'. Posa-hi NOT davant per al contrari: type_1 NOT IN ('fire', 'water', 'grass') retorna 680 files, i 269 + 680 = 949. Ningú s'ha perdut.

BETWEEN inclou els dos extrems: speed BETWEEN 100 AND 120 és el mateix que speed >= 100 AND speed <= 120. Tots dos retornen 126. Amb > i < n'obtindries 83, perquè els Pokémon que tenen exactament 100 i exactament 120 queden fora.

IS NULL troba els blancs: els 439 Pokémon que només tenen un tipus. IS NOT NULL troba els altres 510. Aquest es mereix una secció pròpia.

NULL: la línia en blanc de la carta

NULL vol dir desconegut: aquí no hi ha res escrit. No és zero, i no és un text buit.

Executa això i compta:

SELECT pokemon, type_1, type_2 FROM pokemon WHERE type_2 = NULL;

0 files. Cap error. Han desaparegut els 439 Pokémon d'un sol tipus?

No. Pensa en dues cartes amb una línia en blanc. Els dos blancs són iguals? No ho pots saber: potser hi hauria d'haver alguna cosa, i no saps què. Per tant, quan compares qualsevol cosa amb NULL, la base de dades respon «desconegut», i WHERE només conserva les files on la resposta és un sí clar.

Tu escrius La base de dades respon Files
type_2 = NULL «desconegut» per a totes les files 0
type_2 = '' «no»: un blanc tampoc no és un text buit 0
type_2 IS NULL «sí» per als que són en blanc 439
type_2 IS NOT NULL «sí» per als que tenen valor 510

Per això SQL té una pregunta a part per als blancs. Recorda-ho: IS NULL, mai = NULL.

Respostes errònies que semblen correctes

Totes les trampes d'aquesta pàgina tenen una cosa en comú: cap no dona error. La base de dades fa exactament el que has escrit i retorna un nombre creïble.

Trampa Què obtens Lliçó
LIKE '%mega%' per comptar les evolucions Mega 50 en lloc de 48: meganium i yanmega s'hi colen Un patró coincideix amb el text, no amb el significat. Fes servir '%-mega%' i mira les files, no només el recompte.
= NULL 0 files Els blancs necessiten IS NULL.
Barrejar AND i OR sense parèntesis 79 en lloc de 34 AND s'aplica primer, com × abans de +. Fes servir parèntesis.
LIMIT amb un empat Un top 5 diferent del del teu company Afegeix una segona columna d'ordenació.

La tercera, amb detall. Vols els Pokémon de foc o d'aigua amb un atac superior a 100:

SELECT COUNT(*) FROM pokemon
WHERE type_1 = 'fire' OR type_1 = 'water' AND attack > 100;

79. La base de dades ho ha llegit com «tots els Pokémon de foc (59), més els d'aigua amb atac superior a 100 (20)». Posa parèntesis al voltant de la part de l'OR:

SELECT COUNT(*) FROM pokemon
WHERE (type_1 = 'fire' OR type_1 = 'water') AND attack > 100;

34. Les mateixes paraules, una altra pregunta. (COUNT(*) compta files; és l'entrada de la lliçó següent.)

Prova-ho: la cacera de la Pokédex

Obre SQL Lab i respon cada pregunta amb una sola consulta. Escriu primer la consulta, després obre la solució i compara el teu número.

N. Pregunta
1 Nom i pes dels tres Pokémon més pesats.
2 Quantes evolucions Mega hi ha? El seu nom conté -mega.
3 Quants Pokémon no tenen generació (generation_id)?
4 Elèctrics, d'un sol tipus, amb velocitat de 100 a 120: noms, primer el més ràpid.
5 Nom i atac dels Pokémon d'un sol tipus amb un atac superior a 150, primer el més fort.
Solucions

1 — SELECT pokemon, weight FROM pokemon ORDER BY weight DESC LIMIT 3; → celesteela i cosmoem (999.9), després groudon-primal (999.7).

2 — SELECT COUNT(*) FROM pokemon WHERE pokemon LIKE '%-mega%'; → 48.

3 — SELECT COUNT(*) FROM pokemon WHERE generation_id IS NULL; → 147. Tingues present aquest número: torna a les dues lliçons següents.

4 — SELECT pokemon, speed FROM pokemon WHERE type_1 = 'electric' AND type_2 IS NULL AND speed BETWEEN 100 AND 120 ORDER BY speed DESC; → 6 files, de zebstrika (116) fins a voltorb (100).

5 — SELECT pokemon, attack FROM pokemon WHERE type_2 IS NULL AND attack > 150 ORDER BY attack DESC; → 5 files, la primera deoxys-attack (180).

Si SQL Lab respon amb un quadre vermell, llegeix el missatge: normalment diu la paraula amb què ha ensopegat. Mira just abans d'aquesta paraula per veure si falta una cometa o hi ha una paraula clau mal escrita. I executa una sola consulta cada vegada: si n'hi ha diverses a l'editor, només veus el resultat de l'última.

Idees clau

  • Una consulta té una forma fixa: SELECT (columnes) · FROM (taula) · WHERE (files) · ORDER BY (ordre) · LIMIT (retall).
  • WHERE pregunta una condició a cada fila. El text va entre cometes simples i distingeix majúscules i minúscules; els nombres no porten cometes.
  • AND estreny, OR eixampla. AND s'aplica primer: quan els barrejes, fes servir parèntesis.
  • ORDER BY ... DESC LIMIT n respon «top N», però LIMIT talla els empats arbitràriament.
  • DISTINCT mostra els valors diferents d'una columna: fes-lo servir abans de filtrar.
  • LIKE coincideix amb un patró, IN amb una llista, BETWEEN amb un interval amb els dos extrems, IS NULL amb un blanc. = NULL no és mai cert.
  • Els errors perillosos no donen error. Retornen un nombre que sembla correcte.

Comprovació ràpida

1. Quina consulta retorna només el nom i l'atac de cada Pokémon?
  • SELECT * FROM pokemon;
  • SELECT pokemon, attack FROM pokemon;
  • SELECT pokemon attack FROM pokemon;
  • FROM pokemon SELECT pokemon, attack;

SELECT llista les columnes, separades per comes, i va abans de FROM. * retorna les 22 columnes. Sense la coma, la consulta s'executa però canvia el nom de la primera columna: una columna en lloc de dues.

2. WHERE type_1 = 'Fire' retorna 0 files i cap error. Quin és el motiu més probable?
  • La taula no té cap Pokémon de foc
  • El text ha d'anar entre cometes dobles
  • El text distingeix majúscules i minúscules, i la taula desa fire en minúscules
  • WHERE no pot comparar text

La base de dades ha comparat cada carta amb Fire i cap no hi coincidia, perquè el text distingeix majúscules i minúscules. Zero files no prova que «no n'hi ha»: executa SELECT DISTINCT type_1 per veure com s'escriuen realment els valors.

3. Què retorna SELECT pokemon, speed FROM pokemon ORDER BY speed DESC LIMIT 3;?
  • Tres Pokémon qualssevol
  • Els tres Pokémon més lents
  • Els tres Pokémon més ràpids
  • Tots els Pokémon amb una velocitat superior a 3

ORDER BY speed DESC ordena el munt amb la velocitat més gran a dalt, i LIMIT 3 guarda les tres primeres cartes. Sense ORDER BY, LIMIT 3 donaria unes tres files qualssevol.

4. Per què WHERE type_2 = NULL retorna 0 files?
  • Perquè tots els Pokémon tenen un segon tipus
  • Perquè NULL s'ha d'escriure entre cometes
  • Perquè NULL vol dir desconegut, i per tant una comparació amb ell no és mai un sí clar
  • Perquè = només funciona amb nombres

NULL no és un valor: vol dir «aquí no hi ha res escrit». Comparar-hi dona «desconegut», i WHERE només conserva les files que responen sí. La pregunta correcta és type_2 IS NULL, que troba 439 files.

5. Comptes les evolucions Mega amb LIKE '%mega%' i en surten 50. El nombre real és 48. Què ha passat?
  • LIKE ignora la primera i l'última lletra d'un nom
  • El patró també coincideix amb meganium i yanmega, que no són evolucions Mega
  • % només coincideix amb un caràcter
  • Dues evolucions Mega estan desades dues vegades

Un patró coincideix amb el text, no amb el significat. %mega% vol dir «conté mega en qualsevol lloc». Les evolucions Mega s'escriuen amb un guionet, així que '%-mega%' retorna els 48 correctes. Mira sempre les files, no només el recompte.

6. WHERE type_1 = 'fire' OR type_1 = 'water' AND attack > 100 retorna 79 files. Amb parèntesis al voltant de la part de l'OR en retorna 34. Per què?
  • Els parèntesis fan la consulta més ràpida, i per això arriben menys files
  • OR s'aplica primer, així que la primera versió ignora l'atac
  • AND s'aplica primer: sense parèntesis, la condició de l'atac només s'aplica a l'aigua
  • La primera versió compta alguns Pokémon dues vegades

AND s'aplica abans que OR, com × abans de +. Sense parèntesis obtens els 59 Pokémon de foc més els 20 d'aigua forts. Amb parèntesis, la condició de l'atac s'aplica als dos tipus: 34.

7. Una taula songs té les columnes title, artist, year i streams. Tria les dues consultes que retornen correctament les cinc cançons més reproduïdes del 2020 al 2023.
  • SELECT title, streams FROM songs WHERE year BETWEEN 2020 AND 2023 ORDER BY streams DESC LIMIT 5;
  • SELECT title, streams FROM songs WHERE year >= 2020 AND year <= 2023 ORDER BY streams DESC LIMIT 5;
  • SELECT title, streams FROM songs WHERE year BETWEEN 2020 AND 2023 LIMIT 5;
  • SELECT title, streams FROM songs WHERE year > 2020 AND year < 2023 ORDER BY streams DESC LIMIT 5;

BETWEEN inclou els dos extrems, de manera que equival a >= i <=. La tercera consulta no té ORDER BY: retorna cinc cançons qualssevol. La quarta fa servir > i <, que descarta sense avisar el 2020 i el 2023.

Fins ara cada resposta era una llista de cartes. I si la pregunta és «quants Pokémon de foc hi ha?», i després el mateix per als 18 tipus alhora? Tot seguit: SQL Lab: grups.