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

  1. La jerarquia: servidor, base de dades, esquema, taula
  2. Taules, files i columnes
  3. L'analogia del full de càlcul (i on es trenca)
  4. Tipus de dades numèrics
  5. Per què els diners no van mai en coma flotant
  6. Tipus de text
  7. Tipus de data i hora
  8. Booleans, UUID i JSONB
  9. NULL: l'absència de valor
  10. Equivalències entre PostgreSQL, MySQL i SQLite
  11. Com inspeccionar taules existents
  12. Errors habituals i consells
  13. Exercicis
  14. Conclusió

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

  1. No pots consultar dues bases de dades alhora. Si botigaverda i proves_sql viuen al mateix servidor, una consulta no les pot combinar directament (caldrien extensions com dblink o postgres_fdw). A MySQL, en canvi, sí que és habitual escriure SELECT ... FROM altra_base.taula, perquè allà "base de dades" i "esquema" són gairebé sinònims.
  2. Els esquemes sí que es combinen lliurement. Dins de botigaverda podries tenir un esquema vendes i un altre analitica i 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ó:

SELECT nom FROM productes;
SELECT nom FROM public.productes;

L'ordre de cerca el determina el paràmetre search_path:

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

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

  1. 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.
  2. Cada columna té un únic tipus. No pots desar 'demà' en una columna DATE.
  3. Les files no tenen ordre intrínsec. Una taula és un conjunt. Si no demanes ORDER BY explí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".

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

INSERT INTO productes (nom, preu) VALUES ('Producte estrany', 'dotze euros');
ERROR:  invalid input syntax for type numeric: "dotze euros"

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.

  1. 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 un INTEGER més una seqüència que l'autoincrementa. Des de PostgreSQL 10 la forma recomanada per l'estàndard és GENERATED BY DEFAULT AS IDENTITY, que és la que fa servir l'script del curs. Ho veuràs amb detall al mòdul 5.

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

SELECT 0.1::REAL + 0.2::REAL AS suma_flotant,
       0.1::NUMERIC + 0.2::NUMERIC AS suma_exacta;
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. Mai FLOAT, REAL ni DOUBLE PRECISION. La penalització de velocitat és irrellevant comparada amb un cèntim perdut.

Existeix a més el tipus MONEY a PostgreSQL, però no es recomana: depèn de la configuració regional del servidor i no admet bé diverses divises. Fes servir NUMERIC.

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

SELECT 'ES'::CHAR(5) = 'ES' AS semblen_iguals,
       LENGTH('ES'::CHAR(5)) AS longitud_emmagatzemada;
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, VARCHAR i TEXT (els TEXT es desen fora de la fila i no admeten valor per defecte). El que aquí és indiferent, allà no ho és.

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

  • TIMESTAMP desa 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.
  • TIMESTAMPTZ converteix 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 servir DATE. BotigaVerda fa servir DATE en totes les seves dates perquè són dates de calendari, no instants.

Dialecte: MySQL té DATETIME (sense zona) i TIMESTAMP (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.

  1. Booleans, UUID i JSONB

BOOLEAN

Desa TRUE, FALSE o NULL. Ocupa 1 byte. A BotigaVerda el fan servir productes.actiu i proveidors.actiu.

SELECT TRUE AS cert, FALSE AS fals, NULL::BOOLEAN AS desconegut;

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

SELECT '{"origen": "València", "certificat": "EU-BIO"}'::JSONB -> 'origen' AS origen;
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.

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

SELECT 10 + NULL AS suma,
       'Hola ' || NULL AS concatenacio,
       NULL = NULL AS comparacio;
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 JOIN i els filtres— és la lliçó 04-03. De moment n'hi ha prou amb el concepte.

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

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

botigaverda=# \dt
        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
botigaverda=# \d productes
                            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 propi pg_catalog (pg_tables, pg_class, pg_attribute), més complet i més ràpid, encara que no portable. Les metaordres \d consulten precisament aquest catàleg.

Errors habituals i consells

  • Desar diners en FLOAT o REAL. L'error més car d'aquesta lliçó. Sempre NUMERIC(10,2).
  • Desar dates com a text. VARCHAR per a una data impedeix ordenar bé, calcular diferències i validar. Fes servir DATE o TIMESTAMPTZ.
  • 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 a INTEGER perdràs el zero inicial i el +34.
  • Posar VARCHAR(255) per costum. El 255 ve de MySQL antic. A PostgreSQL fes servir TEXT tret 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 NULL amb 0 o ''. 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 BY no 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.nom es llegeix millor que producte.noms.

Exercicis

Exercici 1

Per a cada dada de BotigaVerda, tria el tipus PostgreSQL més adequat i justifica'l en una línia:

  1. El preu de venda d'un producte.
  2. El codi de país d'un proveïdor en format ISO (ES, PT, FR).
  3. El comentari d'una ressenya.
  4. La puntuació d'una ressenya (1 a 5).
  5. La data en què es va registrar un client.
  6. Si un producte està actiu o no.
  7. L'instant exacte en què es va confirmar un pagament, amb clients en tres països.
  8. 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, UUID i JSONB.
  • La regla innegociable: els diners van en NUMERIC, mai en coma flotant, perquè 0.1 + 0.2 <> 0.3 en binari.
  • NULL com a absència de valor, diferent de 0 i 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 taula i information_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

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