Fins ara totes les teves consultes han retornat totes les files de la taula. Amb 20 productes això és manejable; amb 20 milions, ni el teu terminal ni la teva paciència ho aguantarien. La clàusula WHERE és la que converteix una consulta en una pregunta concreta: no "dona'm els productes", sinó "dona'm els productes de cosmètica que costen més de 10 euros". En aquesta lliçó aprendràs a filtrar files amb comparacions sobre números, text i dates, a combinar-les amb AND, OR i NOT, a manejar columnes booleanes, i a reconèixer dos paranys que produeixen resultats equivocats sense donar cap error: la precedència d'operadors i les comparacions amb valors nuls.
Contingut
WHEREcom a filtre fila a fila- On encaixa
WHEREa l'ordre lògic d'execució - Comparar números
- Comparar text
- Comparar dates
- Comparar booleans
- Combinar condicions:
AND,OR,NOTi els parèntesis - Expressions calculades dins de
WHERE - El símptoma dels nuls
- Errors habituals i consells
- Exercicis
- Conclusió
WHERE com a filtre fila a fila
WHERE com a filtre fila a filaWHERE s'escriu després de FROM i conté una condició: una expressió que, per a cada fila, s'avalua com a certa, falsa o desconeguda.
| id | nom | preu |
|---|---|---|
| 1 | Oli d'oliva verge extra 500 ml | 12.50 |
| 6 | Crema facial d'àloe vera 50 ml | 18.90 |
| 8 | Oli corporal d'ametlles 200 ml | 14.25 |
| 10 | Detergent ecològic concentrat 1 L | 11.20 |
| 13 | Espelmes de cera de soja (pack 2) | 13.75 |
| 15 | Te verd matcha cerimonial 30 g | 22.00 |
| 20 | Càpsules d'espirulina 120 u | 16.40 |
7 files de 20. El mecanisme mental és simple: PostgreSQL recorre les files, avalua preu > 10 en cadascuna i es queda només amb les que donen cert.
I aquí hi ha la clau que resol la meitat dels problemes amb nuls: només passen les files la condició de les quals és certa. Una condició falsa descarta la fila, i una condició desconeguda (NULL) també la descarta. WHERE no és "descarta les falses": és "conserva les certes". Hi tornarem a la secció 9.
Al model relacional de 01-05, això és una selecció (triar files), enfront de la projecció que fa SELECT (triar columnes). Una consulta amb SELECT i WHERE fa les dues coses: retalla la taula amunt i amplada.
- On encaixa
WHERE a l'ordre lògic d'execució
WHERE a l'ordre lògic d'execucióJa pots activar el pas 2 del diagrama de 02-01:
flowchart LR
A["1 · FROM<br/>productes<br/>20 files"] --> B["2 · WHERE<br/>preu > 10<br/>7 files"]
B --> C["3 · SELECT<br/>id, nom, preu"]
C --> D["4 · ORDER BY<br/>(02-05)"]
D --> E["5 · LIMIT<br/>(02-06)"]
D'aquest ordre se'n dedueixen tres conseqüències pràctiques:
| Conseqüència | Explicació |
|---|---|
WHERE no pot fer servir àlies del SELECT |
Quan s'avalua, el SELECT encara no s'ha executat (ho vas veure a 02-02) |
WHERE sí que pot fer servir columnes que no projectes |
WHERE cost > 5 funciona encara que cost no aparegui al SELECT: la columna existeix a la fila que surt de FROM |
| Filtrar aviat és filtrar barat | Com abans descartis files, menys feina hi ha a fer després. És la base de l'optimització del mòdul 8 |
Exemple del segon punt:
| id | nom | preu |
|---|---|---|
| 6 | Crema facial d'àloe vera 50 ml | 18.90 |
| 15 | Te verd matcha cerimonial 30 g | 22.00 |
| 20 | Càpsules d'espirulina 120 u | 16.40 |
La columna cost no és al resultat, però sí que participa en el filtre. Perfectament vàlid.
- Comparar números
Els operadors de comparació que vas veure a 01-03, ara aplicats de debò:
| Operador | Significat | Exemple | Files retornades sobre productes |
|---|---|---|---|
= |
Igual | WHERE categoria_id = 2 |
4 |
<> o != |
Diferent | WHERE categoria_id <> 1 |
15 |
> |
Major | WHERE preu > 10 |
7 |
>= |
Major o igual | WHERE stock >= 100 |
8 |
< |
Menor | WHERE preu < 5 |
7 |
<= |
Menor o igual | WHERE stock <= 50 |
3 |
| id | nom | categoria_id | preu | stock |
|---|---|---|---|---|
| 6 | Crema facial d'àloe vera 50 ml | 2 | 18.90 | 60 |
| 7 | Xampú sòlid de romaní 80 g | 2 | 8.40 | 95 |
| 8 | Oli corporal d'ametlles 200 ml | 2 | 14.25 | 45 |
| 9 | Bàlsam labial de calèndula 15 ml | 2 | 4.60 | 130 |
Tota la categoria "Cosmètica natural". Fixa't que has hagut d'escriure 2 i no 'Cosmètica natural': a productes només hi ha l'identificador. Traduir el 2 al seu nom exigeix la taula categories i per tant un JOIN (mòdul 3).
Un filtre operatiu típic: productes exhaurits.
| id | nom | stock | actiu |
|---|---|---|---|
| 13 | Espelmes de cera de soja (pack 2) | 0 | true |
Només el producte 13, tal com va quedar fixat en dissenyar el conjunt de dades.
Números sense cometes.
WHERE preu > '10'funciona a PostgreSQL perquè el motor converteix la cadena, però és un mal costum: en comparacions més complexes pot impedir que es faci servir un índex i, en altres motors, produeix comparacions alfabètiques on'9' > '10'. EscriuWHERE preu > 10.
- Comparar text
Les cadenes es comparen amb els mateixos operadors, entre cometes simples:
| id | nom | cognoms | ciutat | pais |
|---|---|---|---|---|
| 7 | Sofia | Moreira Costa | Lisboa | Portugal |
| 8 | Tiago | Almeida Nunes | Porto | Portugal |
| id | nom | cognoms | ciutat |
|---|---|---|---|
| 9 | Camille | Dubois | Lió |
| 10 | Julien | Moreau | París |
4.1. Les comparacions de text distingeixen majúscules
Aquest és el parany número u amb dades de text:
Retorna 0 files. No hi ha cap error, no hi ha cap avís: simplement no hi ha cap client el país del qual sigui exactament la cadena 'espanya' en minúscules. Els 11 clients espanyols tenen 'Espanya'.
| id | nom | cognoms | ciutat |
|---|---|---|---|
| 1 | Lucía | Martínez Soler | València |
| 2 | Carlos | Ferrer Ibáñez | València |
| 3 | Marta | Sanchis Gil | Castelló |
| 4 | Javier | Ortega Ruiz | Madrid |
| 5 | Ana | Belmonte Roca | Barcelona |
| 6 | Pau | Llorens Vidal | València |
| 11 | Elena | Navarro Puig | Alacant |
| 12 | Diego | Ramos Herrera | Sevilla |
| 13 | Núria | Bosch Ferrer | Barcelona |
| 14 | Hugo | Iglesias Pardo | Saragossa |
| 15 | Inés | Carrasco Vega | València |
11 files, que amb les 2 de Portugal i les 2 de França sumen els 15 clients.
Recorda la distinció de 01-03: la sintaxi de SQL no distingeix majúscules (select = SELECT), però les dades sí. La forma robusta de comparar ignorant majúscules fa servir funcions de cadena o l'operador ILIKE, i tots dos són matèria dels mòduls 4 i 6:
-- Avanços, no els desenvolupem aquí
WHERE LOWER(pais) = 'espanya' -- funció de cadena, lliçó 06-01
WHERE pais ILIKE 'espanya' -- comparació insensible, lliçó 04-014.2. Els espais compten (i són invisibles)
0 files. Hi ha un espai al final del literal i 'Espanya' ≠ 'Espanya '. Aquest error és especialment traïdor perquè no es veu en llegir la consulta, i apareix constantment quan les dades arriben d'un CSV o d'un formulari web mal sanejat.
Dos matisos que convé conèixer:
- Els espais inicials també compten:
' Espanya'tampoc no coincideix. - Amb el tipus
CHAR(n)(de longitud fixa) l'estàndard SQL emplena amb espais i els ignora en comparar, així que'Espanya'i'Espanya 'sí que serien iguals. És una de les raons per les quals BotigaVerda fa servirVARCHARiTEXT, maiCHAR.
Si sospites d'espais sobrers, un truc ràpid per veure'ls:
| pais_delimitat |
|---|
| [Espanya] |
| [Portugal] |
| [França] |
Els claudàtors delatarien qualsevol espai paràsit. (DISTINCT és el tema de la lliçó següent; aquí només serveix per no repetir quinze vegades els mateixos tres països.) La neteja definitiva d'espais es fa amb TRIM, al mòdul 6.
4.3. Comparacions d'ordre sobre text
Els operadors < i > també funcionen amb cadenes i fan servir l'ordre alfabètic de la col·lació de la base de dades:
| id | nom | cognoms |
|---|---|---|
| 8 | Tiago | Almeida Nunes |
| 5 | Ana | Belmonte Roca |
| 13 | Núria | Bosch Ferrer |
Els tres clients els cognoms dels quals comencen per A o B. És un filtre poc freqüent a la pràctica —per cercar per prefix es fa servir LIKE, a la lliçó 04-01—, però convé saber que existeix. El detall de com la col·lació afecta l'ordre es tracta a la lliçó 02-05.
- Comparar dates
Les dates s'escriuen com a literals de text en format ISO ('AAAA-MM-DD') i PostgreSQL les converteix al tipus DATE de la columna:
| id | client_id | data_comanda | estat |
|---|---|---|---|
| 17 | 7 | 2026-01-13 | enviat |
| 18 | 5 | 2026-01-27 | pagat |
| 19 | 6 | 2026-02-09 | pagat |
| 20 | 9 | 2026-02-21 | pendent |
Les quatre comandes del 2026. Un rang es construeix amb dues comparacions unides per AND:
SELECT id, client_id, data_comanda, estat, despeses_enviament
FROM comandes
WHERE data_comanda >= '2025-06-01'
AND data_comanda < '2025-09-01';| id | client_id | data_comanda | estat | despeses_enviament |
|---|---|---|---|---|
| 7 | 6 | 2025-06-11 | lliurat | 6.50 |
| 8 | 7 | 2025-06-28 | lliurat | 9.90 |
| 9 | 8 | 2025-07-15 | lliurat | 9.90 |
| 10 | 9 | 2025-08-03 | lliurat | 12.50 |
Les comandes de l'estiu del 2025: juny, juliol i agost.
Fixa't en el patró >= inici AND < fi, amb el límit superior exclòs. És la forma professional d'escriure rangs de dates, per dos motius:
- No has de saber si el mes té 28, 30 o 31 dies: hi poses el dia 1 del mes següent.
- Si algun dia aquestes columnes passessin de
DATEaTIMESTAMP,<= '2025-08-31'deixaria fora tot el que hagués passat aquell dia després de mitjanit, mentre que< '2025-09-01'continuaria sent correcte.
Al mòdul 4 veuràs BETWEEN, que escriu rangs de manera més compacta però inclou sempre els dos extrems, amb aquest mateix parany per a les dates amb hora.
Errors típics amb dates, ja advertits a 01-03:
| Escriptura | Què passa |
|---|---|
'2026-01-01' |
Correcte, ISO 8601, sense ambigüitat |
'03/04/2026' |
Depèn de la configuració DateStyle del servidor: pot ser el 3 d'abril o el 4 de març... o fallar |
'2026-13-01' |
ERROR: date/time field value out of range |
2026-01-01 (sense cometes) |
S'interpreta com la resta 2026 - 1 - 1 = 2024 |
Aquesta última mereix veure's:
Sort que dona error: si la columna fos numèrica, hauries filtrat pel 2024 sense adonar-te'n.
- Comparar booleans
productes.actiu i proveidors.actiu són de tipus BOOLEAN. Una condició booleana ja és una condició: no cal comparar-la amb res.
-- Aquestes tres consultes són equivalents
SELECT id, nom FROM productes WHERE actiu;
SELECT id, nom FROM productes WHERE actiu = TRUE;
SELECT id, nom FROM productes WHERE actiu IS TRUE;Les tres retornen 19 files: els 20 productes menys el 20, que està descatalogat.
I per al cas contrari:
| id | nom | preu | actiu |
|---|---|---|---|
| 20 | Càpsules d'espirulina 120 u | 16.40 | false |
| id | nom | pais | actiu |
|---|---|---|---|
| 5 | EcoNordic Supplies | Alemanya | false |
Quina escriure? Comparativa:
| Forma | Llegibilitat | Comportament amb NULL |
Recomanació |
|---|---|---|---|
WHERE actiu |
Molt alta, es llegeix com l'anglès | La fila amb NULL no passa |
Preferida quan la columna és NOT NULL |
WHERE actiu = TRUE |
Alta, explícita | La fila amb NULL no passa |
Acceptable; útil si el lector no domina SQL |
WHERE actiu IS TRUE |
Mitjana | La fila amb NULL no passa (retorna false, no NULL) |
Només si la columna admet nuls i vols una condició que mai no doni NULL |
WHERE NOT actiu |
Alta | La fila amb NULL no passa |
Preferida per al cas negatiu |
WHERE actiu = FALSE |
Alta | Igual que l'anterior | Acceptable |
A BotigaVerda les dues columnes actiu són NOT NULL, així que les diferències són teòriques. En bases de dades reals, una columna booleana que admeti nuls té tres estats possibles (true, false, NULL) i llavors WHERE NOT actiu i WHERE actiu IS NOT TRUE deixen de ser el mateix.
Nota de dialecte: MySQL i SQLite no tenen un tipus booleà real: desen 1 i 0.
WHERE actiufunciona igual perquè qualsevol valor diferent de 0 és cert, peròWHERE actiu = TRUEallà significa literalmentactiu = 1. SQL Server no admetWHERE columnaa seques: exigeixWHERE columna = 1sobre unBIT.
- Combinar condicions:
AND, OR, NOT i els parèntesis
AND, OR, NOT i els parèntesis7.1. AND: s'han de complir totes
| id | nom | ciutat | pais |
|---|---|---|---|
| 1 | Lucía | València | Espanya |
| 2 | Carlos | València | Espanya |
| 6 | Pau | València | Espanya |
| 15 | Inés | València | Espanya |
Quatre clients valencians. Cada AND addicional redueix o manté el nombre de files, mai no l'augmenta.
7.2. OR: n'hi ha prou amb una
| id | client_id | data_comanda | estat |
|---|---|---|---|
| 18 | 5 | 2026-01-27 | pagat |
| 19 | 6 | 2026-02-09 | pagat |
| 20 | 9 | 2026-02-21 | pendent |
Les tres comandes encara no enviades. Cada OR addicional augmenta o manté el nombre de files.
(A 04-02 escriuràs aquesta mateixa condició com a estat IN ('pagat', 'pendent'), que és més curta i més llegible quan la llista creix.)
7.3. NOT: nega una condició
| id | client_id | data_comanda | estat |
|---|---|---|---|
| 6 | 5 | 2025-05-23 | cancellat |
| 16 | 4 | 2025-12-19 | enviat |
| 17 | 7 | 2026-01-13 | enviat |
| 18 | 5 | 2026-01-27 | pagat |
| 19 | 6 | 2026-02-09 | pagat |
| 20 | 9 | 2026-02-21 | pendent |
6 files, que amb les 14 lliurades sumen les 20 comandes. WHERE estat <> 'lliurat' dona exactament el mateix resultat i sol llegir-se millor; NOT brilla quan el que negues és una condició composta: WHERE NOT (a AND b).
7.4. Els parèntesis: l'error que no dona error
AND té més prioritat que OR. Ja ho vas veure a la taula de precedència de 01-03; ara veuràs el mal que causa amb dades reals.
La pregunta de negoci: "dona'm els productes de les categories 1 (Alimentació) o 2 (Cosmètica natural) que costin més de 10 €".
Escrita ingènuament, sense parèntesis:
-- ⚠️ INCORRECTA
SELECT id, nom, categoria_id, preu
FROM productes
WHERE categoria_id = 1
OR categoria_id = 2 AND preu > 10;| id | nom | categoria_id | preu |
|---|---|---|---|
| 1 | Oli d'oliva verge extra 500 ml | 1 | 12.50 |
| 2 | Arròs integral ecològic 1 kg | 1 | 3.90 |
| 3 | Mel de tarongina crua 500 g | 1 | 9.75 |
| 4 | Pasta d'espelta 500 g | 1 | 2.80 |
| 5 | Tomàquet triturat ecològic 400 g | 1 | 1.95 |
| 6 | Crema facial d'àloe vera 50 ml | 2 | 18.90 |
| 8 | Oli corporal d'ametlles 200 ml | 2 | 14.25 |
7 files, i quatre d'elles costen menys de 3 €. El motor ha llegit:
És a dir: tota la categoria 1, sense mirar el preu, més els productes cars de la categoria 2.
Amb parèntesis:
-- ✅ CORRECTA
SELECT id, nom, categoria_id, preu
FROM productes
WHERE (categoria_id = 1 OR categoria_id = 2)
AND preu > 10;| id | nom | categoria_id | preu |
|---|---|---|---|
| 1 | Oli d'oliva verge extra 500 ml | 1 | 12.50 |
| 6 | Crema facial d'àloe vera 50 ml | 2 | 18.90 |
| 8 | Oli corporal d'ametlles 200 ml | 2 | 14.25 |
3 files. Aquesta és la resposta correcta.
Compara l'impacte:
| Versió | Files | Respon a la pregunta? |
|---|---|---|
| Sense parèntesis | 7 | No: inclou 4 productes barats |
| Amb parèntesis | 3 | Sí |
Cap de les dues no dona error. La primera retorna un informe amb dades incorrectes que ningú no detecta fins que algú pregunta per què la pasta d'espelta de 2.80 € apareix al llistat de productes de més de 10 €.
Regla del curs: sempre que en un
WHEREconvisquinANDiOR, posa-hi parèntesis. Encara que coneguis la precedència de memòria, el parèntesi documenta la teva intenció per a qui llegeixi la consulta d'aquí a sis mesos (que probablement seràs tu).
7.5. Un filtre de tres condicions
| id | nom | categoria_id | preu | stock |
|---|---|---|---|---|
| 2 | Arròs integral ecològic 1 kg | 1 | 3.90 | 200 |
| 4 | Pasta d'espelta 500 g | 1 | 2.80 | 150 |
| 5 | Tomàquet triturat ecològic 400 g | 1 | 1.95 | 300 |
| 9 | Bàlsam labial de calèndula 15 ml | 2 | 4.60 | 130 |
| 11 | Fregall vegetal de lufa (pack 3) | 3 | 5.50 | 110 |
| 14 | Infusió de camamilla ecològica 20 u | 4 | 3.25 | 180 |
| 18 | Raspall de dents de bambú | 5 | 3.50 | 240 |
Set productes barats, actius i amb estoc de sobres: candidats perfectes per a una promoció.
- Expressions calculades dins de
WHERE
WHEREPots fer servir a WHERE les mateixes expressions que vas aprendre a 02-02, escrivint-les completes (recorda: l'àlies encara no existeix):
| id | nom | preu | cost | marge |
|---|---|---|---|---|
| 6 | Crema facial d'àloe vera 50 ml | 18.90 | 9.50 | 9.40 |
| 8 | Oli corporal d'ametlles 200 ml | 14.25 | 7.10 | 7.15 |
| 10 | Detergent ecològic concentrat 1 L | 11.20 | 6.00 | 5.20 |
| 12 | Bosses reutilitzables de cotó (pack 5) | 9.90 | 4.30 | 5.60 |
| 13 | Espelmes de cera de soja (pack 2) | 13.75 | 6.90 | 6.85 |
| 15 | Te verd matcha cerimonial 30 g | 22.00 | 12.50 | 9.50 |
| 20 | Càpsules d'espirulina 120 u | 16.40 | 8.70 | 7.70 |
Els set productes que deixen més de 5 € de marge per unitat.
I sobre linies_comanda, les vendes de més de 30 €:
SELECT id,
comanda_id,
producte_id,
quantitat,
ROUND(quantitat * preu_unitari * (1 - descompte), 2) AS import
FROM linies_comanda
WHERE quantitat * preu_unitari * (1 - descompte) > 30;| id | comanda_id | producte_id | quantitat | import |
|---|---|---|---|---|
| 18 | 8 | 1 | 3 | 35.63 |
| 24 | 10 | 6 | 2 | 34.02 |
| 28 | 12 | 15 | 2 | 44.00 |
Només tres línies de les 47 superen els 30 €. Fixa't en la duplicació de l'expressió entre SELECT i WHERE: és lletja però necessària. Les maneres d'evitar-la —subconsultes o CTE— arriben als mòduls 7 i 10.
Avís de rendiment: un filtre com
WHERE preu - cost > 5aplica una operació a cada fila i per tant no pot aprofitar un índex normal sobrepreuni sobrecost. Amb 20 files tant se val; amb 20 milions, no. La solució (índexs sobre expressions) és matèria del mòdul 8.
- El símptoma dels nuls
Deu de les vint comandes de BotigaVerda van entrar per la web i no tenen comercial assignat: el seu empleat_id és NULL. Vegem què passa en filtrar per aquesta columna.
Intent 1: buscar les comandes sense comercial.
Zero files, quan sabem que n'hi ha deu. Sense error, sense avís.
Intent 2: les comandes que NO va gestionar el comercial 4.
El comercial 4 (Óscar Peris) va gestionar les comandes 2, 6, 10 i 16. Caldria esperar que les "no seves" fossin 20 − 4 = 16.
| id | client_id | empleat_id |
|---|---|---|
| 4 | 4 | 5 |
| 8 | 7 | 5 |
| 12 | 10 | 5 |
| 14 | 12 | 6 |
| 18 | 5 | 5 |
| 20 | 9 | 6 |
6 files, no 16. Han desaparegut les deu comandes amb empleat_id nul.
Per què passa. Recorda la regla de la secció 1: WHERE conserva les files la condició de les quals és certa. I qualsevol comparació amb NULL no dona ni cert ni fals: dona desconegut.
| Fila | Condició | S'avalua a | Passa el filtre? |
|---|---|---|---|
Comanda 2 (empleat_id = 4) |
4 <> 4 |
false |
No |
Comanda 4 (empleat_id = 5) |
5 <> 4 |
true |
Sí |
Comanda 1 (empleat_id = NULL) |
NULL <> 4 |
NULL (desconegut) |
No |
"No sé qui va gestionar la comanda 1" no permet concloure que fos diferent del comercial 4. SQL, amb bon criteri lògic, prefereix no afirmar-ho, i la fila cau del resultat.
Com reconèixer el símptoma. Sospita dels nuls quan:
- Una consulta retorna menys files de les que esperaves i la columna del filtre pot ser nul·la.
- Un filtre i la seva negació no sumen el total: aquí
empleat_id = 4dona 4 files iempleat_id <> 4en dona 6; 4 + 6 = 10, no 20. Les deu que falten són les nul·les.
Aquesta comprovació —"el filtre i el seu contrari sumen el total?"— és un dels millors costums que pots adquirir.
Com s'arregla. Amb l'operador IS NULL / IS NOT NULL, que ja va treure el nas a 01-03:
Retorna les 10 files esperades: les comandes 1, 3, 5, 7, 9, 11, 13, 15, 17 i 19.
No ho desenvolupem més aquí: la lògica de tres valors, IS DISTINCT FROM, el comportament dels nuls dins d'AND/OR i les funcions per tractar-los són el contingut complet de la lliçó 04-03. Per ara queda't amb dues coses:
= NULLi<> NULLmai no retornen files. Si els escrius, has comès un error.- Si una columna del
WHEREadmet nuls, pregunta't què vols que passi amb aquestes files abans de donar la consulta per bona.
Errors habituals i consells
- Fer servir un àlies del
SELECTalWHERE.column "..." does not exist. Repeteix l'expressió completa:WHEREs'executa abans queSELECT. - Barrejar
ANDiORsense parèntesis. No dona error; dona un resultat incorrecte. És l'error més car d'aquesta lliçó. - Comparar text sense respectar majúscules o amb espais sobrers. Zero files i cap pista. Comprova els valors reals abans d'acusar la consulta.
- Escriure dates en format local.
'01/03/2026'és ambigu;'2026-03-01'no ho és mai. - Escriure dates sense cometes.
WHERE data_comanda >= 2026-01-01compara amb l'enter 2024. - Fer servir
<=per al final d'un rang de dates. Correcte avui ambDATE, perillós el dia que la columna siguiTIMESTAMP. Fes servir>= inici AND < fi. - Comparar amb
= NULL. Sempre zero files.IS NULL(lliçó 04-03). - Oblidar que un filtre amb
<>exclou els nuls. Comprova que el filtre i la seva negació sumin el total de files. - Consell: construeix el
WHEREper capes. Comença amb una condició, mira quantes files surten, afegeix la següent. Si un salt no quadra, ja saps quina condició has de revisar. - Consell: compta sempre les files. El
(N files)depsqlés el teu millor detector d'errors lògics. - Consell: escriu una condició per línia, amb
AND/ORal principi de la línia i indentats sotaWHERE. Afegir, treure o comentar una condició es torna trivial.
Exercicis
Exercici 1
L'equip de compres vol revisar el catàleg de proveïdors cars. Escriu una consulta que retorni id, nom, preu i stock dels productes actius el preu dels quals superi els 9 € i l'estoc dels quals sigui inferior a 100 unitats. Quantes files esperes i quantes en surten?
Exercici 2
Direcció demana "les comandes del 2025 que estiguin lliurades o cancel·lades i les despeses d'enviament de les quals superin els 5 €". Escriu la consulta correcta, i escriu també la versió sense parèntesis explicant quantes files de més retornaria i per què.
Exercici 3
Sobre empleats, escriu una consulta que retorni els empleats el cap dels quals no sigui l'empleada 1 (Rosa Alcázar Vives), mostrant id, nom, cognoms i cap_id. Després comprova si el nombre de files encaixa amb el total de la taula i explica què ha passat.
Solucions
Solució 1
| id | nom | preu | stock |
|---|---|---|---|
| 3 | Mel de tarongina crua 500 g | 9.75 | 80 |
| 6 | Crema facial d'àloe vera 50 ml | 18.90 | 60 |
| 8 | Oli corporal d'ametlles 200 ml | 14.25 | 45 |
| 10 | Detergent ecològic concentrat 1 L | 11.20 | 70 |
| 12 | Bosses reutilitzables de cotó (pack 5) | 9.90 | 85 |
| 13 | Espelmes de cera de soja (pack 2) | 13.75 | 0 |
| 15 | Te verd matcha cerimonial 30 g | 22.00 | 40 |
7 files. El raonament, condició a condició, sobre els nou productes que costen més de 9 €:
| id | preu > 9 | stock < 100 | actiu | Passa? |
|---|---|---|---|---|
| 1 | 12.50 ✓ | 120 ✗ | ✓ | No |
| 3 | 9.75 ✓ | 80 ✓ | ✓ | Sí |
| 6 | 18.90 ✓ | 60 ✓ | ✓ | Sí |
| 8 | 14.25 ✓ | 45 ✓ | ✓ | Sí |
| 10 | 11.20 ✓ | 70 ✓ | ✓ | Sí |
| 12 | 9.90 ✓ | 85 ✓ | ✓ | Sí |
| 13 | 13.75 ✓ | 0 ✓ | ✓ | Sí |
| 15 | 22.00 ✓ | 40 ✓ | ✓ | Sí |
| 20 | 16.40 ✓ | 55 ✓ | ✗ | No |
La lliçó de l'exercici és doble. Primer, el producte 13 s'hi cola perquè "poc estoc" inclou "gens d'estoc": té 0 unitats i tot i així compleix stock < 100. Si el que busques són productes reposables, el filtre correcte seria stock > 0 AND stock < 100. Segon, el producte 20 queda fora només gràcies a la condició actiu: compleix les altres dues, i si haguessis oblidat aquest filtre hauries proposat reposar un article descatalogat.
Fixa't també que el producte 3 hi entra amb 9.75 €: preu > 9 és estricte, però 9.75 és més gran que 9. Si haguessis volgut "a partir de 10 €" hauries hagut d'escriure preu >= 10, i llavors el 3 i el 12 en caurien.
Solució 2
Versió correcta:
SELECT id,
client_id,
data_comanda,
estat,
despeses_enviament
FROM comandes
WHERE data_comanda >= '2025-01-01'
AND data_comanda < '2026-01-01'
AND (estat = 'lliurat' OR estat = 'cancellat')
AND despeses_enviament > 5;| id | client_id | data_comanda | estat | despeses_enviament |
|---|---|---|---|---|
| 7 | 6 | 2025-06-11 | lliurat | 6.50 |
| 8 | 7 | 2025-06-28 | lliurat | 9.90 |
| 9 | 8 | 2025-07-15 | lliurat | 9.90 |
| 10 | 9 | 2025-08-03 | lliurat | 12.50 |
| 12 | 10 | 2025-10-01 | lliurat | 12.50 |
5 files. La comanda 6 (cancel·lada, 4.95 € de ports) no arriba a 5 €, i les comandes amb ports de 4.95 € o gratuïts queden fora.
Versió sense parèntesis:
-- ⚠️ INCORRECTA
WHERE data_comanda >= '2025-01-01'
AND data_comanda < '2026-01-01'
AND estat = 'lliurat' OR estat = 'cancellat'
AND despeses_enviament > 5;Com que AND s'avalua abans que OR, el motor l'agrupa així:
(data >= '2025-01-01' AND data < '2026-01-01' AND estat = 'lliurat') OR (estat = 'cancellat' AND despeses_enviament > 5)
És a dir: totes les comandes lliurades del 2025 sense mirar els ports (14 de les 14 lliurades són del 2025), més les cancel·lades amb ports superiors a 5 € (cap, perquè l'única cancel·lada té 4.95 €). Total: 14 files en comptes de 5. Nou files de més, totes amb despeses d'enviament que no compleixen el requisit. I, un cop més, sense cap missatge d'error.
Solució 3
| id | nom | cognoms | cap_id |
|---|---|---|---|
| 4 | Óscar | Peris Blasco | 2 |
| 5 | Laia | Puig Sanchis | 2 |
| 6 | Marc | Estévez Roig | 2 |
| 7 | Irene | Salvador Mira | 3 |
4 files. Comprovem l'encaix: la taula té 8 empleats; amb cap_id = 1 n'hi ha 3 (l'Andrés, la Beatriz i el Daniel); amb cap_id <> 1 n'hi ha 4. I 3 + 4 = 7, no 8.
Falta una fila: Rosa Alcázar Vives, la directora general, el cap_id de la qual és NULL. La seva condició NULL <> 1 s'avalua a desconegut, no a cert, així que WHERE la descarta. És exactament el símptoma de la secció 9.
Si el que volies era "tothom llevat de qui depèn directament de la Rosa", inclosa la mateixa Rosa, la consulta correcta fa servir IS NULL (lliçó 04-03):
I aquesta sí que retorna 5 files: els quatre anteriors més la Rosa. La prova que la lògica tanca: 3 (depenen de la Rosa) + 5 (la resta) = 8 empleats.
Conclusió
Ja saps fer preguntes concretes a BotigaVerda:
WHEREfiltra fila a fila i conserva només aquelles la condició de les quals és certa; les falses i les desconegudes es descarten igual.- Ocupa el pas 2 de l'ordre lògic, abans de
SELECT: pot fer servir qualsevol columna de la taula, però mai un àlies de la projecció. - Compares números sense cometes, text entre cometes simples i respectant majúscules i espais, i dates en format ISO amb el patró
>= inici AND < fi. - Amb booleans n'hi ha prou amb
WHERE actiuiWHERE NOT actiu; comparar amb= TRUEés correcte però redundant. - Combines condicions amb
AND,ORiNOT, i saps que barrejarANDiORsense parèntesis produeix resultats incorrectes sense donar cap error. - Pots filtrar per expressions calculades, repetint-les senceres perquè l'àlies encara no existeix.
- Reconeixes el símptoma dels nuls: menys files de les esperades, i un filtre i la seva negació que no sumen el total. La cura,
IS NULL, arriba a la lliçó 04-03.
A la lliçó següent, DISTINCT i eliminació de duplicats, atacaràs un problema diferent: quan projectes només unes poques columnes, apareixen files repetides que a la taula original no ho eren. Veuràs com eliminar-les, per què DISTINCT actua sobre la combinació completa de columnes del SELECT (i no sobre la primera, com molta gent es pensa) i on se situa exactament a l'ordre lògic d'execució.
Curs de SQL
Mòdul 1: Introducció a SQL
- Què és SQL?
- Configurar el teu entorn SQL
- Sintaxi bàsica de SQL
- Entendre bases de dades i taules
- El model relacional: claus primàries i foranes
- La base de dades del curs: BotigaVerda
Mòdul 2: Consultes bàsiques de SQL
- Instrucció SELECT
- Àlies, expressions i columnes calculades
- Filtrar dades amb WHERE
- DISTINCT i eliminació de duplicats
- Ordenar dades amb ORDER BY
- Limitar resultats amb LIMIT
Mòdul 3: Treballar amb múltiples taules
- Operacions JOIN
- INNER JOIN
- LEFT JOIN
- RIGHT JOIN
- FULL OUTER JOIN
- SELF JOIN i CROSS JOIN
- Unions de conjunts: UNION, INTERSECT i EXCEPT
Mòdul 4: Filtratge avançat de dades
- Utilitzar LIKE per a coincidència de patrons
- Operadors IN i BETWEEN
- Valors NULL i IS NULL
- Funcions d'agregació: COUNT, SUM, AVG, MIN i MAX
- Agregar dades amb GROUP BY
- Clàusula HAVING
Mòdul 5: Manipulació de dades
- Crear taules i restriccions amb CREATE TABLE
- Instrucció INSERT
- Instrucció UPDATE
- Instrucció DELETE
- Instrucció UPSERT (MERGE)
- Modificar l'esquema: ALTER TABLE i migracions segures
Mòdul 6: Funcions avançades de SQL
- Funcions de cadena
- Funcions numèriques
- Funcions de data i hora
- Conversió de tipus i tractament dels NULL: CAST i COALESCE
- Expressions condicionals
Mòdul 7: Subconsultes i consultes imbricades
- Introducció a les subconsultes
- Subconsultes correlacionades
- EXISTS i NOT EXISTS
- Utilitzar subconsultes en les clàusules SELECT, FROM i WHERE
- Subconsultes o JOIN: quin triar
Mòdul 8: Índexs i optimització del rendiment
- Entendre els índexs
- Creació i gestió d'índexs
- Tipus d'índex i quan no indexar
- Tècniques d'optimització de consultes
- Anàlisi del rendiment de les consultes
Mòdul 9: Transaccions i concurrència
- Introducció a les transaccions
- Propietats ACID
- Instruccions de control de transaccions
- Nivells d'aïllament i anomalies de concurrència
- Gestió de la concurrència: bloquejos i interbloquejos
Mòdul 10: Temes avançats
- Vistes
- Expressions de taula comunes (CTE)
- Funcions de finestra
- Procediments emmagatzemats
- Disparadors (triggers)
- JSON i dades semiestructurades
Mòdul 11: SQL a la pràctica
- Casos d'ús al món real
- Bones pràctiques
- Seguretat: injecció SQL, permisos i rols
- SQL per a l'anàlisi de dades
- SQL en el desenvolupament web
