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

  1. WHERE com a filtre fila a fila
  2. On encaixa WHERE a l'ordre lògic d'execució
  3. Comparar números
  4. Comparar text
  5. Comparar dates
  6. Comparar booleans
  7. Combinar condicions: AND, OR, NOT i els parèntesis
  8. Expressions calculades dins de WHERE
  9. El símptoma dels nuls
  10. Errors habituals i consells
  11. Exercicis
  12. Conclusió

  1. WHERE com a filtre fila a fila

WHERE 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.

SELECT id,
       nom,
       preu
FROM productes
WHERE preu > 10;
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.

  1. On encaixa 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:

SELECT id, nom, preu
FROM productes
WHERE cost > 8;
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.

  1. 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
SELECT id, nom, categoria_id, preu, stock
FROM productes
WHERE categoria_id = 2;
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.

SELECT id, nom, stock, actiu
FROM productes
WHERE stock = 0;
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'. Escriu WHERE preu > 10.

  1. Comparar text

Les cadenes es comparen amb els mateixos operadors, entre cometes simples:

SELECT id, nom, cognoms, ciutat, pais
FROM clients
WHERE pais = 'Portugal';
id nom cognoms ciutat pais
7 Sofia Moreira Costa Lisboa Portugal
8 Tiago Almeida Nunes Porto Portugal
SELECT id, nom, cognoms, ciutat
FROM clients
WHERE pais = 'França';
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:

SELECT id, nom, pais FROM clients WHERE pais = 'espanya';
(0 files)

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'.

SELECT id, nom, cognoms, ciutat
FROM clients
WHERE pais = '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-01

4.2. Els espais compten (i són invisibles)

SELECT id, nom FROM clients WHERE pais = 'Espanya ';

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 servir VARCHAR i TEXT, mai CHAR.

Si sospites d'espais sobrers, un truc ràpid per veure'ls:

SELECT DISTINCT '[' || pais || ']' AS pais_delimitat FROM clients;
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:

SELECT id, nom, cognoms
FROM clients
WHERE cognoms < 'C';
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.

  1. 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:

SELECT id, client_id, data_comanda, estat
FROM comandes
WHERE data_comanda >= '2026-01-01';
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:

  1. No has de saber si el mes té 28, 30 o 31 dies: hi poses el dia 1 del mes següent.
  2. Si algun dia aquestes columnes passessin de DATE a TIMESTAMP, <= '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:

SELECT id FROM comandes WHERE data_comanda >= 2026-01-01;
ERROR:  operator does not exist: date >= integer

Sort que dona error: si la columna fos numèrica, hauries filtrat pel 2024 sense adonar-te'n.

  1. 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:

SELECT id, nom, preu, actiu
FROM productes
WHERE NOT actiu;
id nom preu actiu
20 Càpsules d'espirulina 120 u 16.40 false
SELECT id, nom, pais, actiu
FROM proveidors
WHERE actiu = 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 actiu funciona igual perquè qualsevol valor diferent de 0 és cert, però WHERE actiu = TRUE allà significa literalment actiu = 1. SQL Server no admet WHERE columna a seques: exigeix WHERE columna = 1 sobre un BIT.

  1. Combinar condicions: AND, OR, NOT i els parèntesis

7.1. AND: s'han de complir totes

SELECT id, nom, ciutat, pais
FROM clients
WHERE pais = 'Espanya'
  AND ciutat = 'València';
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

SELECT id, client_id, data_comanda, estat
FROM comandes
WHERE estat = 'pagat'
   OR estat = 'pendent';
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ó

SELECT id, client_id, data_comanda, estat
FROM comandes
WHERE NOT estat = 'lliurat';
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

ANDmé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:

categoria_id = 1  OR  (categoria_id = 2 AND preu > 10)

É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

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 WHERE convisquin AND i OR, 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

SELECT id, nom, categoria_id, preu, stock
FROM productes
WHERE actiu
  AND preu < 10
  AND stock > 100;
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ó.

  1. Expressions calculades dins de WHERE

Pots fer servir a WHERE les mateixes expressions que vas aprendre a 02-02, escrivint-les completes (recorda: l'àlies encara no existeix):

SELECT id,
       nom,
       preu,
       cost,
       preu - cost AS marge
FROM productes
WHERE preu - cost > 5;
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 > 5 aplica una operació a cada fila i per tant no pot aprofitar un índex normal sobre preu ni sobre cost. Amb 20 files tant se val; amb 20 milions, no. La solució (índexs sobre expressions) és matèria del mòdul 8.

  1. 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.

SELECT id, client_id, empleat_id
FROM comandes
WHERE empleat_id = NULL;
(0 files)

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.

SELECT id, client_id, empleat_id
FROM comandes
WHERE empleat_id <> 4;
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
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 = 4 dona 4 files i empleat_id <> 4 en 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:

SELECT id, client_id, empleat_id
FROM comandes
WHERE empleat_id IS NULL;

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:

  1. = NULL i <> NULL mai no retornen files. Si els escrius, has comès un error.
  2. Si una columna del WHERE admet 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 SELECT al WHERE. column "..." does not exist. Repeteix l'expressió completa: WHERE s'executa abans que SELECT.
  • Barrejar AND i OR sense 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-01 compara amb l'enter 2024.
  • Fer servir <= per al final d'un rang de dates. Correcte avui amb DATE, perillós el dia que la columna sigui TIMESTAMP. 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 WHERE per 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) de psql és el teu millor detector d'errors lògics.
  • Consell: escriu una condició per línia, amb AND/OR al principi de la línia i indentats sota WHERE. 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

SELECT id,
       nom,
       preu,
       stock
FROM productes
WHERE actiu
  AND preu > 9
  AND stock < 100;
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 ✓
6 18.90 ✓ 60 ✓
8 14.25 ✓ 45 ✓
10 11.20 ✓ 70 ✓
12 9.90 ✓ 85 ✓
13 13.75 ✓ 0 ✓
15 22.00 ✓ 40 ✓
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

SELECT id,
       nom,
       cognoms,
       cap_id
FROM empleats
WHERE cap_id <> 1;
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):

SELECT id, nom, cognoms, cap_id
FROM empleats
WHERE cap_id <> 1
   OR cap_id IS NULL;

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:

  • WHERE filtra 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 actiu i WHERE NOT actiu; comparar amb = TRUE és correcte però redundant.
  • Combines condicions amb AND, OR i NOT, i saps que barrejar AND i OR sense 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

Mòdul 2: Consultes bàsiques de SQL

Mòdul 3: Treballar amb múltiples taules

Mòdul 4: Filtratge avançat de dades

Mòdul 5: Manipulació de dades

Mòdul 6: Funcions avançades de SQL

Mòdul 7: Subconsultes i consultes imbricades

Mòdul 8: Índexs i optimització del rendiment

Mòdul 9: Transaccions i concurrència

Mòdul 10: Temes avançats

Mòdul 11: SQL a la pràctica

Mòdul 12: Projecte final

© Copyright 2026. Tots els drets reservats