Vas tancar el mòdul 1 amb la base de dades de BotigaVerda carregada i verificada, i amb una promesa: la primera consulta real era a una lliçó de distància. Ja hi som. En aquesta lliçó aprendràs la instrucció més important de SQL, la que faràs servir el 90 % de les vegades que t'asseguis davant d'una base de dades: SELECT. Veuràs com demanar columnes concretes, per què SELECT * és còmode per explorar però perillós en producció, què és realment el conjunt de resultats que et retorna el motor i —el més important a mitjà termini— en quin ordre lògic executa PostgreSQL les clàusules d'una consulta, que no és l'ordre en què les escrius. Aquest últim punt sembla teòric avui i serà la clau per entendre els mòduls 4 i 7.

Contingut

  1. La consulta mínima: SELECT ... FROM ...
  2. Seleccionar columnes concretes
  3. SELECT *: quan sí i per què en producció no
  4. L'ordre de les columnes el decideix el SELECT
  5. SELECT sense FROM: SQL com a calculadora
  6. Què és realment el conjunt de resultats
  7. L'ordre lògic d'execució d'una consulta
  8. Llegir resultats a psql i el mode expandit \x
  9. Errors habituals i consells
  10. Exercicis
  11. Conclusió

  1. La consulta mínima: SELECT ... FROM ...

Una consulta de lectura necessita, com a mínim, dues clàusules:

SELECT nom
FROM categories;
nom
Alimentació
Cosmètica natural
Llar sostenible
Begudes
Higiene personal
Complements

Llegeix-la en veu alta al revés i entendràs millor el que fa el motor: "de la taula categories, dona'm la columna nom".

Clàusula Paper
SELECT <llista de columnes> Projecció: quines columnes vols al resultat
FROM <taula> Origen: d'on surten les files

En l'argot del model relacional que vas veure a 01-05, SELECT fa una projecció (triar columnes) i, quan hi afegim WHERE a la lliçó 02-03, farem també una selecció (triar files). Que la paraula clau es digui SELECT i faci la projecció és una de les petites incoherències històriques de SQL; convé saber-ho per no confondre's en llegir literatura acadèmica.

Fixa't que no li has dit a PostgreSQL com recórrer la taula, ni en quin fitxer és, ni si convé fer servir un índex. Només has descrit el què. Aquest és el caràcter declaratiu del llenguatge que vam veure a 01-01.

  1. Seleccionar columnes concretes

Per demanar diverses columnes se separen amb comes:

SELECT id,
       nom,
       preu
FROM productes;
id nom preu
1 Oli d'oliva verge extra 500 ml 12.50
2 Arròs integral ecològic 1 kg 3.90
3 Mel de tarongina crua 500 g 9.75
4 Pasta d'espelta 500 g 2.80
5 Tomàquet triturat ecològic 400 g 1.95
6 Crema facial d'àloe vera 50 ml 18.90
7 Xampú sòlid de romaní 80 g 8.40
8 Oli corporal d'ametlles 200 ml 14.25
9 Bàlsam labial de calèndula 15 ml 4.60
10 Detergent ecològic concentrat 1 L 11.20
11 Fregall vegetal de lufa (pack 3) 5.50
12 Bosses reutilitzables de cotó (pack 5) 9.90
13 Espelmes de cera de soja (pack 2) 13.75
14 Infusió de camamilla ecològica 20 u 3.25
15 Te verd matcha cerimonial 30 g 22.00
16 Kombutxa de gingebre 750 ml 4.95
17 Suc de taronja premsat en fred 1 L 5.40
18 Raspall de dents de bambú 3.50
19 Desodorant natural en barra 50 g 7.80
20 Càpsules d'espirulina 120 u 16.40

20 files. Exactament les 20 que vas carregar a 01-06: SELECT sense filtre retorna totes les files de la taula, i projecta només les columnes que has anomenat. Ni cost, ni stock, ni actiu, ni data_alta hi apareixen: no els has demanat.

Detalls d'escriptura que ja coneixes de 01-03 i que aquí s'apliquen:

  • La coma va entre columnes, mai darrere de l'última. SELECT id, nom, FROM productes dona syntax error at or near "FROM".
  • Posar una columna per línia no és cap caprici: quan la llista creixi fins a quinze columnes, agrairàs poder afegir-ne o treure'n una sense reescriure la línia sencera.
  • Els noms han d'existir. Si escrius SELECT preu_venda FROM productes, PostgreSQL respon column "preu_venda" does not exist. Un \d productes et treu de dubtes en un segon.

  1. SELECT *: quan sí i per què en producció no

L'asterisc significa "totes les columnes de la taula, en l'ordre en què es van definir":

SELECT * FROM categories;
id nom descripcio
1 Alimentació Productes ecològics d'alimentació seca i conserves
2 Cosmètica natural Cosmètica amb ingredients naturals i sense parabens
3 Llar sostenible Neteja i parament amb materials reutilitzables
4 Begudes Infusions, sucs i fermentats ecològics
5 Higiene personal Higiene diària amb envasos reduïts o compostables
6 Complements Suplements alimentaris d'origen vegetal

És còmode, i per explorar una taula que no coneixes és el primer que s'escriu. El problema apareix quan aquest * acaba dins d'una aplicació, d'un informe o d'una vista.

Risc de SELECT * en producció Què passa a la pràctica
Contracte fràgil Si demà algú afegeix una columna a productes, el teu codi rep una columna extra que no espera; si algú les reordena, un accés per posició et retorna la dada equivocada
Trànsit i memòria innecessaris Portes descripcio (TEXT) encara que només vulguis nom. Multiplicat per milions de files, són diners
Trenca optimitzacions Un índex que conté totes les columnes que demanes permet un index-only scan (mòdul 8). Amb * gairebé mai no és possible
Il·legible en revisió Qui llegeixi la consulta no sap quines dades necessita realment
Ambigüitat amb JOIN Amb dues taules unides, * retorna dues columnes anomenades nom i cap manera neta de distingir-les (mòdul 3)

Regla del curs: SELECT * per explorar a psql; llista explícita de columnes en qualsevol consulta que vulguis desar, versionar o executar més d'una vegada.

Hi ha una variant intermèdia que sí que és habitual i correcta: SELECT taula.* en consultes amb diverses taules, per dir "totes les columnes d'aquesta taula". La veuràs al mòdul 3.

  1. L'ordre de les columnes el decideix el SELECT

El resultat no ha de respectar per força l'ordre físic de la taula. Mana la teva llista:

SELECT descripcio,
       nom,
       id
FROM categories;
descripcio nom id
Productes ecològics d'alimentació seca i conserves Alimentació 1
Cosmètica amb ingredients naturals i sense parabens Cosmètica natural 2
Neteja i parament amb materials reutilitzables Llar sostenible 3
Infusions, sucs i fermentats ecològics Begudes 4
Higiene diària amb envasos reduïts o compostables Higiene personal 5
Suplements alimentaris d'origen vegetal Complements 6

I res no t'impedeix repetir una columna, encara que rarament sigui útil:

SELECT id, id, nom FROM categories;

Retorna tres columnes, dues de les quals es diuen id. PostgreSQL ho permet; els clients i els llenguatges de programació que hi accedeixen per nom, no sempre. És un avís que, tan bon punt hi hagi noms repetits, faran falta àlies, que és justament el tema de la lliçó següent.

  1. SELECT sense FROM: SQL com a calculadora

A PostgreSQL la clàusula FROM és opcional. Sense ella, SELECT avalua expressions i retorna una única fila:

SELECT 2 + 2;
?column?
4

Aquesta capçalera ?column? significa "no sé com anomenar això": l'expressió no té nom. S'arregla amb un àlies (lliçó 02-02).

Exemples que faràs servir cada dia per provar coses abans de ficar-les en una consulta gran:

SELECT current_date;                     -- data del servidor
SELECT 12.50 * 1.21;                     -- preu amb IVA
SELECT 3 * 12.50 * (1 - 0.05);           -- import d'una línia amb 5 % de descompte
SELECT 'Lucía' || ' ' || 'Martínez';     -- concatenació de text
current_date
2026-08-02
?column?
15.1250
?column?
35.6250
?column?
Lucía Martínez

Tres coses que ja es veuen aquí i que reapareixeran:

  1. current_date retorna la data del dia en què executis la consulta; el valor de dalt és només un exemple.
  2. 12.50 * 1.21 dona 15.1250, amb quatre decimals. PostgreSQL suma les escales dels operands en multiplicar NUMERIC. Per presentar diners caldrà arrodonir (lliçó 02-02).
  3. 3 * 12.50 * (1 - 0.05) és el càlcul de l'import de la línia 18 de linies_comanda, amb els números escrits a mà. A la lliçó següent l'escriuràs amb columnes en comptes de literals.

Nota de dialecte: Oracle exigeix un FROM, i per això allà s'escriu SELECT 2+2 FROM dual;. MySQL, SQL Server, SQLite i PostgreSQL admeten SELECT sense FROM.

  1. Què és realment el conjunt de resultats

El que retorna una consulta s'anomena conjunt de resultats (result set) i, conceptualment, és una relació: una taula temporal, sense nom, que existeix només mentre dura la consulta. D'aquí se'n deriven tres propietats que convé interioritzar avui:

  1. És una taula com qualsevol altra. Té columnes amb nom i tipus, i files. Per això podrà fer-se servir com a origen d'una altra consulta (subconsultes al FROM, mòdul 7) o combinar-se amb una altra relació (UNION, mòdul 3).
  2. No modifica res. SELECT és una operació de només lectura: per moltes vegades que l'executis, la taula original no canvia. Modificar dades és el mòdul 5.
  3. No té ordre garantit. Aquesta és la que més sorprèn.

Sobre el tercer punt: al resultat de la secció 2 els productes van sortir ordenats per id, de l'1 al 20. Sembla que la taula "està ordenada". No ho està. Aquest ordre és un efecte col·lateral que les files es van inserir seguides i que PostgreSQL les va llegir en ordre físic. Tan bon punt la taula creixi, s'actualitzin files, entri en joc un índex o el motor faci servir diversos processos en paral·lel, l'ordre pot canviar sense avís i sense que res falli.

La regla és taxativa i no admet excepcions:

Si l'ordre importa, escriu ORDER BY. Si no l'escrius, no tens dret a esperar cap ordre concret.

La clàusula ORDER BY és la lliçó 02-05. Fins llavors, quan en aquesta lliçó o a la següent vegis resultats "ordenats per id", entén que és l'ordre que probablement veuràs, no el que el motor et promet.

  1. L'ordre lògic d'execució d'una consulta

Escrius una consulta en un ordre i el motor la resol en un altre. Entendre aquesta diferència és el que després fa evidents coses que altrament semblen arbitràries (per què un àlies funciona a ORDER BY però no a WHERE, per què HAVING existeix a més de WHERE, per què una subconsulta pot veure certes columnes i unes altres no).

Ordre en què s'escriu:

SELECT   columnes
FROM     taula
WHERE    condició
ORDER BY columnes
LIMIT    n;

Ordre en què s'executa lògicament:

flowchart LR
    A["1 · FROM<br/>d'on surten les files"] --> B["2 · WHERE<br/>quines files es queden"]
    B --> C["3 · SELECT<br/>quines columnes es projecten"]
    C --> D["4 · ORDER BY<br/>en quin ordre es retornen"]
    D --> E["5 · LIMIT<br/>quantes se'n lliuren"]
Pas Clàusula Què fa Lliçó
1 FROM Determina el conjunt de files de partida 02-01
2 WHERE Descarta files que no compleixen la condició 02-03
3 SELECT Calcula i projecta les columnes del resultat 02-01 / 02-02
4 ORDER BY Ordena el resultat ja projectat 02-05
5 LIMIT Retalla quantes files es lliuren 02-06

De moment només fas servir els passos 1 i 3, així que el diagrama sembla exagerat. Desa'l: anirem afegint clàusules a aquest mateix esquema a cada lliçó del mòdul, i al mòdul 4 hi incorporarem GROUP BY i HAVING.

Dues conseqüències que ja pots anticipar:

  • Com que WHERE s'executa abans que SELECT, quan arribi el moment de filtrar no podràs fer servir a WHERE un nom inventat al SELECT: encara no existeix.
  • Com que ORDER BY s'executa després de SELECT, allà sí que el podràs fer servir.

Un matís important: això és l'ordre lògic, el que defineix el significat de la consulta. El pla real d'execució que tria l'optimitzador pot ser molt diferent (llegir un índex, filtrar mentre llegeix, aturar-se abans d'hora), sempre que el resultat sigui el mateix que el de l'ordre lògic. Els plans reals s'estudien al mòdul 8 amb EXPLAIN.

  1. Llegir resultats a psql i el mode expandit \x

Quan executes una consulta a psql veus una cosa així:

botigaverda=> SELECT id, nom, preu FROM productes LIMIT 6;
 id |              nom               | preu
----+--------------------------------+-------
  1 | Oli d'oliva verge extra 500 ml |  12.50
...
(6 files)

Peces d'aquesta sortida:

Element Significat
Primera línia Noms de les columnes del resultat
Línia de guionets Separador
Alineació Els números s'alineen a la dreta, el text a l'esquerra: és una pista visual del tipus de dada
(6 files) Quantes files ha retornat la consulta. Mira-ho sempre: és la primera comprovació de si la teva consulta fa el que creus
Cel·la buida Un NULL es mostra com un espai en blanc (configurable amb \pset null '(null)')

Els booleans s'imprimeixen com a t i f, no com a true/false. En aquest curs els escriurem com a true/false a les taules de resultats per llegibilitat.

Quan una fila té moltes columnes o textos llargs, la sortida es desmunta i es torna il·legible. Per a això hi ha el mode expandit:

botigaverda=> \x
Expanded display is on.
botigaverda=> SELECT * FROM clients;
-[ RECORD 1 ]--+---------------------------
id             | 1
nom            | Lucía
cognoms        | Martínez Soler
email          | [email protected]
ciutat         | València
pais           | Espanya
data_registre  | 2025-01-10
referit_per_id |
-[ RECORD 2 ]--+---------------------------
id             | 2
nom            | Carlos
cognoms        | Ferrer Ibáñez
email          | [email protected]
ciutat         | València
pais           | Espanya
data_registre  | 2025-01-22
referit_per_id | 1

Cada fila passa a ocupar un bloc vertical. Es veu perfectament que el client 1 té referit_per_id buit (és NULL: va arribar pel seu compte) i que el client 2 va ser referit pel client 1.

\x alterna entre activat i desactivat; \x auto deixa que psql decideixi segons l'amplada del terminal, i és l'opció més còmoda per al dia a dia.

Altres metaordres que t'estalviaran temps en aquest mòdul:

Metaordre Per a què
\d productes Recordar els noms exactes de les columnes
\x auto Mode expandit només quan calgui
\timing on Veure quant triga cada consulta (útil des del mòdul 8)
\e Editar l'última consulta al teu editor de text
\g Reexecutar l'última consulta

  1. Primeres consultes reals sobre BotigaVerda

Tanca la lliçó practicant la projecció sobre les tres taules que més faràs servir.

Qui són els clients i d'on són:

SELECT nom,
       cognoms,
       ciutat,
       pais
FROM clients;
nom cognoms ciutat pais
Lucía Martínez Soler València Espanya
Carlos Ferrer Ibáñez València Espanya
Marta Sanchis Gil Castelló Espanya
Javier Ortega Ruiz Madrid Espanya
Ana Belmonte Roca Barcelona Espanya
Pau Llorens Vidal València Espanya
Sofia Moreira Costa Lisboa Portugal
Tiago Almeida Nunes Porto Portugal
Camille Dubois Lió França
Julien Moreau París França
Elena Navarro Puig Alacant Espanya
Diego Ramos Herrera Sevilla Espanya
Núria Bosch Ferrer Barcelona Espanya
Hugo Iglesias Pardo Saragossa Espanya
Inés Carrasco Vega València Espanya

L'equip, amb la seva jerarquia en brut:

SELECT id,
       nom,
       cognoms,
       carrec,
       cap_id
FROM empleats;
id nom cognoms carrec cap_id
1 Rosa Alcázar Vives Directora general (null)
2 Andrés Company Talens Responsable de vendes 1
3 Beatriz Nadal Ripoll Responsable de logística 1
4 Óscar Peris Blasco Comercial 2
5 Laia Puig Sanchis Comercial 2
6 Marc Estévez Roig Atenció al client 2
7 Irene Salvador Mira Operària de magatzem 3
8 Daniel Vercher Lluch Analista de dades 1

Aquí tens el NULL de la Rosa Alcázar Vives, l'única sense cap. Veure'l com una cel·la buida és el teu primer contacte pràctic amb els nuls: el mòdul 4 els dedica una lliçó sencera.

L'estat de les comandes:

SELECT id,
       client_id,
       data_comanda,
       estat
FROM comandes;
id client_id data_comanda estat
1 1 2025-03-04 lliurat
2 2 2025-03-12 lliurat
3 3 2025-04-02 lliurat
4 4 2025-04-19 lliurat
5 1 2025-05-07 lliurat
6 5 2025-05-23 cancellat
7 6 2025-06-11 lliurat
8 7 2025-06-28 lliurat
9 8 2025-07-15 lliurat
10 9 2025-08-03 lliurat
11 2 2025-09-09 lliurat
12 10 2025-10-01 lliurat
13 11 2025-10-22 lliurat
14 12 2025-11-14 lliurat
15 1 2025-12-02 lliurat
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

Observa que client_id és un número, no un nom. Per saber que la comanda 10 és de la Camille Dubois cal anar a clients, i això exigeix combinar dues taules: és exactament el que fa el JOIN del mòdul 3. Fins llavors treballarem sempre amb una taula cada vegada.

Errors habituals i consells

  • Coma sobrera abans de FROM. SELECT id, nom, FROM productessyntax error at or near "FROM". L'error assenyala FROM, però la fallada és a la coma anterior (regla del "mira el token anterior" de 01-03).
  • Coma que falta entre columnes. SELECT nom preu FROM productes no dona error: PostgreSQL entén preu com a àlies de nom i retorna una sola columna anomenada preu amb els noms dels productes. És una fallada silenciosa; compta sempre les columnes del resultat.
  • column "..." does not exist. Gairebé sempre és una errada o una columna que és en una altra taula. \d taula abans de reescriure a cegues.
  • Confondre les cometes. SELECT "nom" FROM productes funciona (identificador en minúscules); SELECT 'nom' FROM productes retorna 20 files amb el text literal nom. No és el mateix.
  • Suposar que el resultat ve ordenat. Funciona avui amb 20 files i falla el dia que la taula en tingui un milió. ORDER BY o res.
  • Deixar SELECT * al codi. Fes-lo servir per explorar i substitueix-lo per la llista de columnes tan bon punt la consulta sigui definitiva.
  • Consell: mira sempre el recompte de files. Si n'esperaves 20 i psql diu (0 files), has après alguna cosa abans de llegir ni una sola cel·la.
  • Consell: prova les expressions sense FROM. SELECT 3 * 12.50 * (1 - 0.05); valida el càlcul en un segon, sense soroll de taules.
  • Consell: activa \x auto. És la diferència entre llegir una fila de clients i barallar-te amb el terminal.

Exercicis

Exercici 1

Escriu una consulta que retorni el nom, el país i l'email de tots els proveïdors. Després respon: quantes files retorna i per què no cal cap filtre per obtenir-les totes?

Exercici 2

Sobre empleats, escriu una consulta que mostri les columnes en aquest ordre exacte: carrec, cognoms, nom, ciutat. Justifica per què el resultat no coincideix amb l'ordre en què les columnes estan definides a la taula.

Exercici 3

Sense fer servir cap taula, calcula amb SELECT aquestes tres coses i explica el resultat:

  1. El preu amb IVA (21 %) del producte 15 (Te verd matcha cerimonial, 22.00 €).
  2. L'import de la línia 27 de linies_comanda: 8 unitats a 1.95 € amb un descompte de 0.15.
  3. El marge brut del producte 6 (preu 18.90, cost 9.50).

Solucions

Solució 1

SELECT nom,
       pais,
       email
FROM proveidors;
nom pais email
Huerta del Turia Espanya [email protected]
BioSierra Ibérica Espanya [email protected]
Verde Atlántico Portugal [email protected]
Maison Nature França [email protected]
EcoNordic Supplies Alemanya [email protected]

Retorna 5 files. Una consulta sense WHERE no descarta cap fila: el pas 2 de l'ordre lògic simplement no existeix, així que tot el que surt de FROM arriba a SELECT. Fixa't que el proveïdor 5 hi apareix encara que tingui actiu = FALSE: no hi ha res que l'exclogui. Filtrar-lo serà feina de la lliçó 02-03.

Solució 2

SELECT carrec,
       cognoms,
       nom,
       ciutat
FROM empleats;
carrec cognoms nom ciutat
Directora general Alcázar Vives Rosa València
Responsable de vendes Company Talens Andrés València
Responsable de logística Nadal Ripoll Beatriz València
Comercial Peris Blasco Óscar València
Comercial Puig Sanchis Laia Castelló
Atenció al client Estévez Roig Marc València
Operària de magatzem Salvador Mira Irene València
Analista de dades Vercher Lluch Daniel València

El raonament: la taula defineix les columnes com a id, nom, cognoms, carrec, cap_id, salari, data_contractacio, ciutat, però aquest ordre només el fa servir SELECT *. Quan enumeres columnes, l'ordre del resultat és el de la teva llista, perquè la projecció construeix una relació nova amb la forma que tu decideixes. És la mateixa raó per la qual pots ometre columnes o repetir-les.

Solució 3

SELECT 22.00 * 1.21          AS iva_matcha,
       8 * 1.95 * (1 - 0.15) AS import_linia_27,
       18.90 - 9.50          AS marge_producte_6;
iva_matcha import_linia_27 marge_producte_6
26.6200 13.2600 9.40

Tres observacions sobre el raonament:

  1. 22.00 * 1.21 dona 26.6200, no 26.62. En multiplicar dos NUMERIC, PostgreSQL suma les escales: dos decimals per dos decimals en donen quatre. Per a un informe caldrà arrodonir.
  2. El descompte és una fracció, així que 0.15 és el 15 % i el factor que s'aplica és (1 - 0.15) = 0.85. Escriure 8 * 1.95 * 0.15 donaria el descompte, no l'import. És l'error d'interpretació més freqüent amb aquesta base de dades.
  3. La resta 18.90 - 9.50 manté dos decimals, perquè en la suma i la resta l'escala del resultat és la més gran de les dues, no la suma.

(Sí, hem fet servir AS abans d'hora: sense ell les tres columnes es dirien ?column?. És la primera clàusula de la lliçó següent.)

Conclusió

Ja saps interrogar una taula:

  • La consulta mínima és SELECT columnes FROM taula;: FROM diu d'on surten les files i SELECT quines columnes es projecten.
  • SELECT * serveix per explorar, però en producció se substitueix per la llista explícita de columnes: contracte estable, menys trànsit i millors plans d'execució.
  • L'ordre de les columnes del resultat el decideixes tu amb la teva llista, no la definició de la taula.
  • SELECT sense FROM converteix PostgreSQL en una calculadora per provar expressions abans d'integrar-les en una consulta.
  • El conjunt de resultats és una relació temporal de només lectura i sense ordre garantit: si l'ordre importa, ORDER BY.
  • Coneixes l'ordre lògic d'execució FROM → WHERE → SELECT → ORDER BY → LIMIT, que anirem completant a cada lliçó d'aquest mòdul.
  • Saps llegir la sortida de psql, comptar files i fer servir el mode expandit \x quan les files són amples.

A la lliçó següent, Àlies, expressions i columnes calculades, deixaràs de limitar-te a retornar el que hi ha desat i començaràs a calcular: preus amb IVA, marges, marges percentuals i l'import d'una línia de comanda, l'expressió que t'acompanyarà durant tot el curs. De passada posaràs un nom decent a aquestes columnes amb AS i descobriràs, gràcies a l'ordre lògic que acabes d'aprendre, per què un àlies funciona en uns llocs i en uns altres no.

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