Ja saps què és SQL i com s'escriu. Toca ara entendre sobre què actua: l'estructura on viuen les dades. En aquesta lliçó desmuntarem la jerarquia completa —servidor, base de dades, esquema, taula, fila, columna—, veurem per què l'analogia amb un full de càlcul ajuda al principi però es trenca de seguida, i recorrerem en detall el catàleg de tipus de dades de PostgreSQL. Aquest últim punt és més important del que sembla: triar malament un tipus és un error que es paga durant anys, i el cas dels diners desats en coma flotant és l'exemple canònic de per què. Acabarem aprenent a inspeccionar taules que ja existeixen, una habilitat que necessitaràs cada vegada que t'enfrontis a una base de dades aliena.
Contingut
- La jerarquia: servidor, base de dades, esquema, taula
- Taules, files i columnes
- L'analogia del full de càlcul (i on es trenca)
- Tipus de dades numèrics
- Per què els diners no van mai en coma flotant
- Tipus de text
- Tipus de data i hora
- Booleans, UUID i JSONB
- NULL: l'absència de valor
- Equivalències entre PostgreSQL, MySQL i SQLite
- Com inspeccionar taules existents
- Errors habituals i consells
- Exercicis
- Conclusió
- La jerarquia: servidor, base de dades, esquema, taula
A PostgreSQL els objectes s'organitzen en quatre nivells:
graph TD
A["Servidor / Clúster<br/>(un procés, un port: 5432)"] --> B["Base de dades: botigaverda"]
A --> B2["Base de dades: postgres"]
A --> B3["Base de dades: proves_sql"]
B --> C["Esquema: public"]
B --> C2["Esquema: information_schema"]
C --> D["Taula: productes"]
C --> D2["Taula: clients"]
C --> D3["Taula: comandes"]
D --> E["Columnes: id, nom, preu…"]
D --> E2["Files: cada producte concret"]
| Nivell | Què és | Exemple al curs |
|---|---|---|
| Servidor (clúster) | El procés PostgreSQL en marxa, escoltant en un port | El contenidor pg-curs a localhost:5432 |
| Base de dades | Un conjunt aïllat de dades. Una connexió només en veu una alhora | botigaverda |
| Esquema | Un espai de noms dins de la base de dades | public |
| Taula | Una col·lecció de files amb la mateixa estructura | productes |
| Columna | Un camp amb nom i tipus | preu NUMERIC(10,2) |
| Fila | Un registre concret | El producte "Mel de tarongina crua 500 g" |
Dues conseqüències pràctiques d'aquesta jerarquia:
- No pots consultar dues bases de dades alhora. Si
botigaverdaiproves_sqlviuen al mateix servidor, una consulta no les pot combinar directament (caldrien extensions comdblinkopostgres_fdw). A MySQL, en canvi, sí que és habitual escriureSELECT ... FROM altra_base.taula, perquè allà "base de dades" i "esquema" són gairebé sinònims. - Els esquemes sí que es combinen lliurement. Dins de
botigaverdapodries tenir un esquemavendesi un altreanaliticai consultar-los junts sense cap problema.
L'esquema public
Tota base de dades PostgreSQL neix amb un esquema anomenat public. Si crees una taula sense indicar esquema, va a parar allà, i si la consultes sense prefix es busca allà. Aquestes dues sentències són equivalents a la nostra configuració:
L'ordre de cerca el determina el paràmetre search_path:
| search_path |
|---|
| "$user", public |
Significa: "busca primer un esquema que es digui com l'usuari connectat; si no existeix, busca a public". Els esquemes serveixen per organitzar bases grans (separar per àrea funcional, per client, per entorn) i per evitar col·lisions de noms. Tota BotigaVerda viu a public, així que no ens en tornarem a preocupar.
- Taules, files i columnes
Una taula és una col·lecció de files que comparteixen la mateixa estructura. Cada taula es defineix per:
- Un nom (
productes). - Un conjunt ordenat de columnes, cadascuna amb nom i tipus de dada.
- Opcionalment, restriccions que limiten quins valors són vàlids (mòdul 5).
Una vista simplificada de productes:
| id | nom | categoria_id | preu | stock | actiu |
|---|---|---|---|---|---|
| 1 | Oli d'oliva verge extra 500 ml | 1 | 12.50 | 120 | true |
| 2 | Arròs integral ecològic 1 kg | 1 | 3.90 | 200 | true |
| 6 | Crema facial d'àloe vera 50 ml | 2 | 18.90 | 60 | true |
Terminologia formal davant de la col·loquial:
| Terme col·loquial | Terme del model relacional | Què és |
|---|---|---|
| Taula | Relació | El conjunt de dades |
| Fila / registre | Tupla | Una entitat concreta |
| Columna / camp | Atribut | Una propietat d'aquesta entitat |
| Tipus de la columna | Domini | El conjunt de valors vàlids |
Tres propietats fonamentals que convé gravar des del principi:
- Totes les files tenen les mateixes columnes. No hi ha cap fila "amb un camp extra". Si una dada no s'aplica, s'hi desa
NULL. - Cada columna té un únic tipus. No pots desar
'demà'en una columnaDATE. - Les files no tenen ordre intrínsec. Una taula és un conjunt. Si no demanes
ORDER BYexplícitament, el motor te les pot retornar en qualsevol ordre, i aquest ordre pot canviar demà. És un error clàssic refiar-se de l'"ordre natural".
- L'analogia del full de càlcul (i on es trenca)
Al principi ajuda pensar en una taula com un full d'Excel: la primera fila són les capçaleres (columnes) i les següents, les dades (files). L'analogia funciona... fins a cert punt.
| Aspecte | Full de càlcul | Taula de base de dades |
|---|---|---|
| Tipus | Cada cel·la pot contenir el que sigui | Tota la columna comparteix un únic tipus, verificat pel motor |
| Ordre de les files | És visible i significatiu | No hi ha ordre implícit; es demana amb ORDER BY |
| Relacions | Se simulen amb BUSCARV i referències fràgils |
Són de primera classe: claus foranes amb integritat garantida |
| Mida | Milers o centenars de milers de files | Milions o milers de milions |
| Concurrència | Un usuari alhora (o conflictes de versió) | Centenars d'usuaris simultanis amb transaccions |
| Integritat | Res no impedeix teclejar "dotze euros" en una columna d'imports | Restriccions que rebutgen la dada invàlida |
| Fórmules | Desades a les cel·les | La dada es desa; el càlcul es fa en consultar |
| Desfer | Ctrl+Z | ROLLBACK d'una transacció (mòdul 9) |
La diferència conceptual més profunda és la tipificació estricta. A Excel, una columna de preus pot tenir 12,50, 12.50, "12,50 €" i dotze amb cinquanta, i ningú no t'avisa fins que la suma surt malament. A PostgreSQL, si la columna és NUMERIC(10,2), el motor rebutja qualsevol cosa que no sigui un número:
Aquest error, que pot semblar una molèstia, és exactament el valor que aporta una base de dades: les dades incorrectes no arriben a entrar.
- Tipus de dades numèrics
| Tipus | Mida | Rang / Precisió | Quan fer-lo servir |
|---|---|---|---|
SMALLINT |
2 bytes | -32 768 a 32 767 | Comptadors molt petits, edats |
INTEGER (INT) |
4 bytes | ±2 147 483 647 | Per defecte per a enters: ids, stock, quantitats |
BIGINT |
8 bytes | ±9,2 × 10¹⁸ | Ids de taules enormes, comptadors massius |
NUMERIC(p,s) / DECIMAL(p,s) |
Variable | Exacte, fins a 131 072 dígits | Diners i qualsevol càlcul exacte |
REAL |
4 bytes | ~6 dígits decimals | Magnituds científiques aproximades |
DOUBLE PRECISION |
8 bytes | ~15 dígits decimals | Càlculs científics, coordenades |
SERIAL / BIGSERIAL |
4/8 bytes | Enter autoincremental | Claus primàries (vegeu la nota) |
Sobre NUMERIC(p,s):
p(precisió) és el nombre total de dígits.s(escala) és quants d'aquests dígits van després de la coma.NUMERIC(10,2)admet fins a 99 999 999,99 → vuit dígits enters i dos decimals.
A BotigaVerda fem servir NUMERIC(10,2) per a preu, cost, despeses_enviament, preu_unitari, import i salari, i NUMERIC(4,2) per a descompte (una fracció entre 0 i 1, amb dos decimals).
Nota sobre
SERIAL: no és un tipus real, sinó una drecera que crea unINTEGERmés una seqüència que l'autoincrementa. Des de PostgreSQL 10 la forma recomanada per l'estàndard ésGENERATED BY DEFAULT AS IDENTITY, que és la que fa servir l'script del curs. Ho veuràs amb detall al mòdul 5.
- Per què els diners no van mai en coma flotant
REAL i DOUBLE PRECISION emmagatzemen els números en coma flotant binària (estàndard IEEE 754). El problema és que molts decimals que en base 10 són exactes, en base 2 són periòdics: 0,1 en binari és infinit, igual que 1/3 ho és en decimal. L'ordinador en desa una aproximació, i aquests errors minúsculs s'acumulen.
Comprova-ho:
| suma_flotant | suma_exacta |
|---|---|
| 0.30000001 | 0.3 |
I el cas que t'arruïnaria un tancament comptable:
SELECT (0.1::DOUBLE PRECISION + 0.2::DOUBLE PRECISION) = 0.3 AS son_iguals_flotant,
(0.1::NUMERIC + 0.2::NUMERIC) = 0.3 AS son_iguals_exacte;| son_iguals_flotant | son_iguals_exacte |
|---|---|
| false | true |
Portat a BotigaVerda: si desàvem preu com a REAL i sumàvem les 47 línies de comanda de la base de dades, el total podria sortir 143,20999999998 en lloc de 143,21. Multiplica-ho per milers de comandes al mes i tindràs un descompensament comptable impossible de justificar.
| Tipus | Naturalesa | Velocitat | Exactitud | Ús correcte |
|---|---|---|---|---|
REAL / DOUBLE PRECISION |
Aproximada (binària) | Molt ràpida | No exacta | Física, estadística, coordenades, mitjanes aproximades |
NUMERIC(p,s) |
Exacta (decimal) | Més lenta | Exacta | Diners, percentatges comptables, quantitats facturables |
Regla sense excepcions: diners →
NUMERIC. MaiFLOAT,REALniDOUBLE PRECISION. La penalització de velocitat és irrellevant comparada amb un cèntim perdut.Existeix a més el tipus
MONEYa PostgreSQL, però no es recomana: depèn de la configuració regional del servidor i no admet bé diverses divises. Fes servirNUMERIC.
- Tipus de text
| Tipus | Descripció | Quan fer-lo servir |
|---|---|---|
VARCHAR(n) |
Text de llargada variable amb un màxim de n caràcters |
Quan el límit és una regla de negoci real |
TEXT |
Text de llargada il·limitada | L'opció per defecte a PostgreSQL |
CHAR(n) |
Llargada fixa; omple amb espais fins a n |
Gairebé mai. Només per a codis de llargada fixa estricta |
Una particularitat de PostgreSQL que sorprèn qui ve d'altres motors: TEXT i VARCHAR tenen exactament el mateix rendiment. Internament són el mateix tipus; VARCHAR(n) només hi afegeix una comprovació de llargada. No hi ha cap avantatge de velocitat a posar-hi un límit.
Aleshores, quan posar VARCHAR(n)? Quan el límit signifiqui alguna cosa:
pais VARCHAR(60): nom de país, un límit raonable.email VARCHAR(120): hi ha un màxim pràctic conegut.descripcio TEXT: no sabem quant hi escriurà ningú.comentari TEXT: una ressenya pot ser llarga.
Evita CHAR(n). Omple amb espais a la dreta i provoca comparacions sorprenents:
| semblen_iguals | longitud_emmagatzemada |
|---|---|
| true | 2 |
El valor es desa com a 'ES ' però en comparar-lo s'ignoren els espais finals, cosa que genera confusió constant en exportar o concatenar.
Nota de dialecte: a MySQL sí que hi ha diferències de rendiment i emmagatzematge entre
CHAR,VARCHARiTEXT(elsTEXTes desen fora de la fila i no admeten valor per defecte). El que aquí és indiferent, allà no ho és.
- Tipus de data i hora
| Tipus | Què desa | Exemple | Ús a BotigaVerda |
|---|---|---|---|
DATE |
Només la data | 2026-02-14 |
data_comanda, data_registre, data_alta, data_contractacio |
TIME |
Només l'hora | 18:30:00 |
Horaris d'obertura |
TIMESTAMP |
Data i hora, sense zona horària | 2026-02-14 18:30:00 |
Quan la zona és irrellevant |
TIMESTAMPTZ |
Data i hora amb zona horària | 2026-02-14 18:30:00+01 |
Marques d'auditoria, esdeveniments reals |
INTERVAL |
Una durada | 3 days, 2 hours 30 minutes |
Terminis de lliurament |
La distinció entre TIMESTAMP i TIMESTAMPTZ és la que més problemes causa en producció:
TIMESTAMPdesa literalment el que li dones. Si un client francès i un altre de català registren "18:30", es desen igual encara que siguin moments diferents.TIMESTAMPTZconverteix a UTC en desar i a la zona del client en llegir. Representa un instant real del temps.
SELECT NOW() AS ara_amb_zona,
NOW()::TIMESTAMP AS ara_sense_zona,
CURRENT_DATE AS avui,
AGE(DATE '2026-02-14', DATE '2025-11-14') AS diferencia;| ara_amb_zona | ara_sense_zona | avui | diferencia |
|---|---|---|---|
| 2026-02-25 10:14:07.412+01 | 2026-02-25 10:14:07.412 | 2026-02-25 | 3 mons |
Regla pràctica: si el moment té rellevància real (quan va passar alguna cosa), fes servir
TIMESTAMPTZ. Si és una data de calendari sense hora (data d'una comanda, data de naixement), fes servirDATE. BotigaVerda fa servirDATEen totes les seves dates perquè són dates de calendari, no instants.Dialecte: MySQL té
DATETIME(sense zona) iTIMESTAMP(amb conversió a UTC, però limitat fins al 2038). SQLite no té tipus de data: desa text ISO, números o julian days, i les funcions de data operen sobre aquestes representacions.
- Booleans, UUID i JSONB
BOOLEAN
Desa TRUE, FALSE o NULL. Ocupa 1 byte. A BotigaVerda el fan servir productes.actiu i proveidors.actiu.
Aquest NULL té sentit de negoci: "actiu = TRUE" és un producte a la venda, "FALSE" un de descatalogat, i NULL significaria "encara no ho hem decidit". Per això els booleans en SQL tenen tres estats, no dos.
UUID
Un identificador universal de 128 bits, de l'estil a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11. Ocupa 16 bytes.
| Avantatge | Inconvenient |
|---|---|
| Es pot generar al client sense consultar el servidor | Ocupa 4 vegades més que un INTEGER |
| No revela quants registres tens | Il·legible per a humans |
| Únic entre sistemes diferents (útil en microserveis) | Pitjor localitat d'índex: insercions més lentes |
BotigaVerda fa servir INTEGER per als seus id perquè és una base petita, didàctica i on poder escriure WHERE id = 7 és un avantatge d'aprenentatge.
JSONB
PostgreSQL permet desar documents JSON en una columna. JSON desa el text tal qual; JSONB el desa en format binari indexat, i és el que es fa servir a la pràctica. Serveix per a dades semiestructurades amb un esquema variable (atributs específics de cada producte, respostes d'una passarel·la de pagament, configuracions).
| origen |
|---|
| "València" |
Ho esmentem aquí perquè sàpigues que existeix; el seu ús complet es tracta a la lliçó 10-06. I un advertiment: JSONB no és una excusa per no dissenyar bé les teves taules. El que és estructurat va en columnes.
- NULL: l'absència de valor
NULL no és zero, ni cadena buida, ni FALSE. Significa "aquí no hi ha valor" o "no se sap".
A BotigaVerda hi ha tres nuls amb significat de negoci ben definit:
| Columna | Què significa el seu NULL |
|---|---|
comandes.empleat_id |
La comanda va arribar per la web; cap comercial no la va gestionar |
clients.referit_per_id |
El client va arribar pel seu compte, ningú no el va referir |
empleats.cap_id |
És la direcció general: no té superior |
L'essencial ara és entendre que NULL es propaga per qualsevol operació:
| suma | concatenacio | comparacio |
|---|---|---|
| (null) | (null) | (null) |
Qualsevol càlcul que toqui un NULL retorna NULL, perquè operar amb una cosa desconeguda produeix una cosa desconeguda. I com ja vas veure a la lliçó anterior, per comprovar si una cosa és nul·la es fa servir IS NULL, mai = NULL.
Distingeix aquests tres casos, que no són el mateix:
| Valor | Significat |
|---|---|
NULL |
No hi ha dada / es desconeix |
0 |
Hi ha dada, i val zero |
'' (cadena buida) |
Hi ha dada, i és un text sense caràcters |
El tractament complet dels nuls —com afecten les agregacions, els
JOINi els filtres— és la lliçó 04-03. De moment n'hi ha prou amb el concepte.
- Equivalències entre PostgreSQL, MySQL i SQLite
Si has de portar un esquema o llegir codi aliè, aquesta taula t'estalviarà temps:
| Concepte | PostgreSQL 16 | MySQL 8 | SQLite 3 |
|---|---|---|---|
| Enter | INTEGER, BIGINT |
INT, BIGINT |
INTEGER |
| Autoincremental | GENERATED AS IDENTITY / SERIAL |
AUTO_INCREMENT |
INTEGER PRIMARY KEY AUTOINCREMENT |
| Decimal exacte | NUMERIC(p,s) |
DECIMAL(p,s) |
NUMERIC (afinitat, sense garantia) |
| Coma flotant | REAL, DOUBLE PRECISION |
FLOAT, DOUBLE |
REAL |
| Text curt | VARCHAR(n) |
VARCHAR(n) |
TEXT |
| Text llarg | TEXT |
TEXT, LONGTEXT |
TEXT |
| Boolean | BOOLEAN (real) |
TINYINT(1) (àlies) |
Sense tipus: 0 / 1 |
| Data | DATE |
DATE |
TEXT amb format ISO |
| Data i hora | TIMESTAMP, TIMESTAMPTZ |
DATETIME, TIMESTAMP |
TEXT / INTEGER |
| JSON | JSONB (binari, indexable) |
JSON |
TEXT + funcions JSON1 |
| UUID | UUID (natiu) |
CHAR(36) o BINARY(16) |
TEXT |
| Sistema de tipus | Estricte | Estricte (amb mode estricte actiu) | Dinàmic: gairebé tot s'accepta |
La diferència més perillosa és a l'última fila. SQLite fa servir afinitat de tipus: si declares una columna INTEGER i hi insereixes el text 'hola', el desa sense protestar. És còmode per prototipar i desastrós per garantir integritat. És la raó principal per la qual aquest curs fa servir PostgreSQL.
- Com inspeccionar taules existents
Quan arribes a un projecte nou, el primer és entendre'n l'esquema. Hi ha dos camins.
11.1. Metaordres de psql (ràpid)
List of relations Schema | Name | Type | Owner --------+----------------+-------+---------- public | categories | table | curs_sql public | clients | table | curs_sql public | comandes | table | curs_sql public | devolucions | table | curs_sql public | empleats | table | curs_sql public | linies_comanda | table | curs_sql public | productes | table | curs_sql public | proveidors | table | curs_sql public | ressenyes | table | curs_sql
Table "public.productes"
Column | Type | Nullable | Default
--------------+-----------------------+----------+------------------------------
id | integer | not null | generated by default as identity
nom | character varying(150)| not null |
categoria_id | integer | |
proveidor_id | integer | |
preu | numeric(10,2) | not null |
cost | numeric(10,2) | |
stock | integer | not null | 0
actiu | boolean | not null | true
data_alta | date | not null | CURRENT_DATE
Indexes:
"productes_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"productes_categoria_id_fkey" FOREIGN KEY (categoria_id) REFERENCES categories(id)
"productes_proveidor_id_fkey" FOREIGN KEY (proveidor_id) REFERENCES proveidors(id)
Referenced by:
TABLE "linies_comanda" CONSTRAINT ... FOREIGN KEY (producte_id) REFERENCES productes(id)Una sola ordre et dona columnes, tipus, nul·labilitat, valors per defecte, índexs, claus foranes i quines altres taules apunten a aquesta. Fes servir \d+ productes per veure a més la mida i els comentaris.
| Metaordre | Què mostra |
|---|---|
\dt |
Taules de l'esquema actual |
\d nom_taula |
Estructura completa d'una taula |
\d+ nom_taula |
L'anterior més mida, estadístiques i comentaris |
\dn |
Esquemes |
\di |
Índexs |
\dv |
Vistes |
\l+ |
Bases de dades amb la seva mida |
11.2. information_schema (portable i consultable)
L'estàndard SQL defineix un esquema de metadades anomenat information_schema, disponible a PostgreSQL, MySQL i SQL Server. El seu avantatge sobre \d és que és SQL normal: el pots filtrar, ordenar i fer servir des de qualsevol llenguatge.
SELECT column_name,
data_type,
character_maximum_length,
numeric_precision,
numeric_scale,
is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'productes'
ORDER BY ordinal_position;| column_name | data_type | character_maximum_length | numeric_precision | numeric_scale | is_nullable |
|---|---|---|---|---|---|
| id | integer | (null) | 32 | 0 | NO |
| nom | character varying | 150 | (null) | (null) | NO |
| categoria_id | integer | (null) | 32 | 0 | YES |
| proveidor_id | integer | (null) | 32 | 0 | YES |
| preu | numeric | (null) | 10 | 2 | NO |
| cost | numeric | (null) | 10 | 2 | YES |
| stock | integer | (null) | 32 | 0 | NO |
| actiu | boolean | (null) | (null) | (null) | NO |
| data_alta | date | (null) | (null) | (null) | NO |
Vistes útils d'information_schema:
| Vista | Conté |
|---|---|
information_schema.tables |
Totes les taules i vistes |
information_schema.columns |
Totes les columnes amb els seus tipus |
information_schema.table_constraints |
Restriccions (PK, FK, UNIQUE, CHECK) |
information_schema.key_column_usage |
Quines columnes participen en cada clau |
I una consulta que faràs servir sovint per fer-te un mapa ràpid d'una base desconeguda:
SELECT table_name, COUNT(*) AS num_columnes
FROM information_schema.columns
WHERE table_schema = 'public'
GROUP BY table_name
ORDER BY table_name;| table_name | num_columnes |
|---|---|
| categories | 3 |
| clients | 8 |
| comandes | 7 |
| devolucions | 5 |
| empleats | 8 |
| linies_comanda | 6 |
| productes | 9 |
| proveidors | 5 |
| ressenyes | 6 |
information_schemaés l'estàndard, però PostgreSQL té a més el seu catàleg propipg_catalog(pg_tables,pg_class,pg_attribute), més complet i més ràpid, encara que no portable. Les metaordres\dconsulten precisament aquest catàleg.
Errors habituals i consells
- Desar diners en
FLOAToREAL. L'error més car d'aquesta lliçó. SempreNUMERIC(10,2). - Desar dates com a text.
VARCHARper a una data impedeix ordenar bé, calcular diferències i validar. Fes servirDATEoTIMESTAMPTZ. - Desar números que no són quantitats com a números. Un codi postal (
03001), un telèfon o un NIF són text: si els deses com aINTEGERperdràs el zero inicial i el+34. - Posar
VARCHAR(255)per costum. El 255 ve de MySQL antic. A PostgreSQL fes servirTEXTtret que hi hagi un límit de negoci real. - Fer servir
CHAR(n). L'ompliment amb espais provoca errors subtils. Pràcticament mai no és l'elecció correcta. - Confondre
NULLamb0o''. Són tres coses diferents i es comporten de manera diferent en filtres i agregacions. - Refiar-te de l'ordre de les files. Sense
ORDER BYno hi ha ordre garantit, per molt que avui surtin ordenades. - Consell: tria el tipus pensant a cinc anys vista. Canviar el tipus d'una columna amb milions de files en producció és una operació delicada (mòdul 5).
- Consell:
\d taulaés la teva primera ordre en qualsevol base aliena. Abans d'escriure una consulta, mira'n l'estructura. - Consell: posa noms de taula en plural i de columna en singular.
productes.nomes llegeix millor queproducte.noms.
Exercicis
Exercici 1
Per a cada dada de BotigaVerda, tria el tipus PostgreSQL més adequat i justifica'l en una línia:
- El preu de venda d'un producte.
- El codi de país d'un proveïdor en format ISO (
ES,PT,FR). - El comentari d'una ressenya.
- La puntuació d'una ressenya (1 a 5).
- La data en què es va registrar un client.
- Si un producte està actiu o no.
- L'instant exacte en què es va confirmar un pagament, amb clients en tres països.
- El descompte aplicat a una línia de comanda (fracció de 0 a 1, dos decimals).
Exercici 2
Executa aquestes expressions i explica què demostra cadascuna:
SELECT 1.0 / 3.0 AS a;
SELECT (1.0 / 3.0)::REAL AS b;
SELECT 100000000.0::REAL + 1 AS c;
SELECT 'abc'::CHAR(6) || '|' AS d;
SELECT NULL + 5 AS e;Exercici 3
Escriu una consulta sobre information_schema que mostri totes les columnes de tipus numeric de la base de dades botigaverda, indicant en quina taula són i amb quina precisió i escala. Ordena-les per taula i per posició dins de la taula.
Solucions
Solució 1
| Dada | Tipus | Justificació |
|---|---|---|
| 1. Preu de venda | NUMERIC(10,2) |
Són diners: exigeixen aritmètica decimal exacta |
| 2. Codi de país ISO | CHAR(2) o VARCHAR(2) |
Llargada fixa coneguda. És l'única situació on CHAR es defensa; VARCHAR(2) evita l'ompliment amb espais. (BotigaVerda desa el nom complet del país, així que fa servir VARCHAR(60)) |
| 3. Comentari de ressenya | TEXT |
Llargada impredictible; sense límit de negoci |
| 4. Puntuació 1-5 | SMALLINT (o INTEGER) |
Enter molt petit. El rang 1-5 es garanteix amb una restricció CHECK (mòdul 5), no amb el tipus |
| 5. Data de registre | DATE |
Data de calendari, sense hora rellevant |
| 6. Producte actiu | BOOLEAN |
Dos estats més el desconegut |
| 7. Instant del pagament | TIMESTAMPTZ |
És un instant real i hi ha diverses zones horàries implicades |
| 8. Descompte | NUMERIC(4,2) |
Valor exacte entre 0,00 i 1,00; intervé en càlculs d'import |
Solució 2
| Expressió | Resultat | Què demostra |
|---|---|---|
1.0 / 3.0 |
0.33333333333333333333 |
Els literals decimals són numeric: PostgreSQL conserva molts dígits exactes |
(1.0/3.0)::REAL |
0.33333334 |
REAL només desa uns 6-7 dígits significatius: hi ha pèrdua d'informació |
100000000.0::REAL + 1 |
100000000 |
El +1 desapareix: REAL no té prou precisió per distingir 100 000 000 de 100 000 001. És l'argument definitiu contra fer servir coma flotant per a diners |
'abc'::CHAR(6) || '|' |
abc| |
Encara que CHAR(6) omple fins a 6 caràcters, la concatenació elimina els espais finals. Comportament inconsistent que justifica evitar CHAR |
NULL + 5 |
(null) |
NULL es propaga: qualsevol operació aritmètica amb un nul dona nul |
Solució 3
SELECT table_name,
column_name,
numeric_precision,
numeric_scale,
is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
AND data_type = 'numeric'
ORDER BY table_name, ordinal_position;Resultat esperat sobre BotigaVerda:
| table_name | column_name | numeric_precision | numeric_scale | is_nullable |
|---|---|---|---|---|
| comandes | despeses_enviament | 10 | 2 | NO |
| devolucions | import | 10 | 2 | YES |
| empleats | salari | 10 | 2 | YES |
| linies_comanda | preu_unitari | 10 | 2 | NO |
| linies_comanda | descompte | 4 | 2 | NO |
| productes | preu | 10 | 2 | NO |
| productes | cost | 10 | 2 | YES |
Observa que data_type retorna 'numeric' en minúscules, i que la precisió i l'escala vénen en columnes separades: information_schema normalitza els tipus al vocabulari de l'estàndard SQL, no al nom que vas escriure en crear la taula.
Conclusió
En aquesta lliçó has recorregut l'estructura sobre la qual actua SQL:
- La jerarquia servidor → base de dades → esquema (
public) → taula → columnes i files, i per què no es poden consultar dues bases de dades alhora a PostgreSQL. - Una taula és un conjunt de files amb la mateixa estructura, sense ordre intrínsec, amb un tipus per columna verificat pel motor: aquí hi ha la gran diferència amb un full de càlcul.
- El catàleg de tipus de PostgreSQL: numèrics (
INTEGER,BIGINT,NUMERIC(p,s),REAL), text (TEXT,VARCHAR(n),CHAR(n)), data i hora (DATE,TIMESTAMP,TIMESTAMPTZ,INTERVAL),BOOLEAN,UUIDiJSONB. - La regla innegociable: els diners van en
NUMERIC, mai en coma flotant, perquè0.1 + 0.2 <> 0.3en binari. NULLcom a absència de valor, diferent de0i de'', que es propaga en tota operació.- Les equivalències de tipus entre PostgreSQL, MySQL i SQLite, i el perill del sistema de tipus dinàmic de SQLite.
- Com inspeccionar una base aliena amb
\dt,\d taulaiinformation_schema.columns.
A la lliçó següent, El model relacional: claus primàries i foranes, veurem com les taules deixen d'estar aïllades i es relacionen entre si: què és una clau primària i per què fem servir id subrogats, com les claus foranes garanteixen que no hi hagi comandes de clients inexistents, què passa en esborrar un registre del qual depenen d'altres, com es representen les cardinalitats 1:1, 1:N i N:M —amb linies_comanda com a exemple real de taula pont— i com la normalització explica per què l'esquema de BotigaVerda està partit en nou taules i no en una de sola.
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
