Les set taules de BiblioRed existeixen i són buides. Aquesta lliçó les omple i ensenya a interrogar-les. És la lliçó més pràctica del mòdul: aquí aprens les quatre operacions que cobreixen el 90 % de la feina diària amb una base de dades —inserir, consultar, modificar i esborrar—, conegudes col·lectivament com a CRUD (Create, Read, Update, Delete).
Ens limitem deliberadament a una sola taula per consulta. Recompondre informació repartida entre diverses taules és un tema amb prou substància per a la seva pròpia lliçó (02-04), i agrupar i resumir, per a la següent (02-05). Aquí construïm els fonaments: sense dominar WHERE i ORDER BY sobre una taula, cap JOIN no sortirà bé.
El lliurable de la lliçó és el joc de dades de prova de BiblioRed: quatre sucursals, vuit autors, deu socis, nou llibres, quinze exemplars, dotze préstecs i cinc reserves. Tots ficticis i tots coherents entre si. Els farem servir fins al final del curs, així que executa'ls amb atenció i guarda'ls en un fitxer.
Contingut
INSERT: donar d'alta files- Lliurable: el joc de dades de BiblioRed
- Reajustar els generadors d'identificadors
SELECT: projecció, àlies iDISTINCTWHERE: filtrar filesORDER BY: ordenar el resultatLIMITiOFFSET: paginacióUPDATE: modificar files existentsDELETE: eliminar filesTRUNCATE: buidar una taula sencera- Errors habituals i consells
- Exercicis
- Conclusió
INSERT: donar d'alta files
INSERT: donar d'alta filesForma bàsica
Comencem per les sucursals de BiblioRed. La xarxa opera a la ciutat fictícia de Vallmar i té quatre biblioteques.
INSERT INTO sucursals (nom, adreca, telefon, data_obertura)
VALUES ('Centre', 'Plaça Major 1', '900 100 001', '1998-04-12');La sortida de PostgreSQL es llegeix així: INSERT <oid> <nombre de files inserides>. El primer número és un vestigi històric i sempre val 0; el segon és el que importa.
Fixa't en tres coses:
- No hem donat
sucursal_id. La columna ésGENERATED BY DEFAULT AS IDENTITY, així que PostgreSQL li assigna l'1. - La data va entre cometes simples, en format ISO
AAAA-MM-DD. El gestor la converteix aDATEperquè la columna és d'aquest tipus. - El telèfon va entre cometes, encara que sembli un número: és
VARCHAR, com vam decidir a la lliçó anterior.
Sense llista de columnes: la forma fràgil
SQL permet ometre la llista de columnes si dónes un valor per a totes, en l'ordre exacte de la definició de la taula:
-- Legal, però no ho facis
INSERT INTO sucursals VALUES (DEFAULT, 'Nord', 'Avinguda del Parc 45', '900 100 002', '2005-09-30');Per què evitar-ho? Perquè el dia que algú afegeixi una columna amb ALTER TABLE, o reordeni la definició, tots els INSERT sense llista de columnes es trencaran o, pitjor, inseriran valors a la columna equivocada sense donar error. Escriu sempre la llista de columnes. És la primera regla d'higiene del SQL professional.
Fem-ho bé:
INSERT INTO sucursals (nom, adreca, telefon, data_obertura)
VALUES ('Nord', 'Avinguda del Parc 45', '900 100 002', '2005-09-30');Aquesta és la sucursal Nord, que a partir d'ara tindrà sucursal_id = 2.
Inserir diverses files de cop
Es poden encadenar diverses tuples de valors separades per comes. És més ràpid (una sola operació en lloc de N) i més llegible:
INSERT INTO sucursals (nom, adreca, telefon, data_obertura) VALUES
('Sud', 'Carrer Olivar 12', '900 100 003', '2011-02-18'),
('Est', 'Ronda del Port 8', NULL, '2019-06-25');La sucursal Est encara no té telèfon propi: escrivim NULL sense cometes. Si escrivíssim 'NULL' estaríem guardant la cadena de quatre lletres N-U-L-L, que no és el mateix en absolut.
Una altra opció és ometre la columna de la llista: el que no s'esmenta queda a NULL (o al seu valor per defecte, si en tingués).
-- (Només il·lustratiu, NO ho executis: crearia una segona sucursal Est.)
-- Equivalent per al telèfon, però explícit és millor que implícit.
INSERT INTO sucursals (nom, adreca, data_obertura)
VALUES ('Est', 'Ronda del Port 8', '2019-06-25');RETURNING: saber quin identificador s'ha generat
Quan deixes que el gestor generi la clau, sorgeix un problema pràctic immediat: quin número li ha tocat? El necessitaràs per inserir les files filles. PostgreSQL ho resol amb RETURNING:
INSERT INTO autors (nom, cognoms, nacionalitat, any_naixement)
VALUES ('Félix J.', 'Palma', 'espanyola', 1968)
RETURNING autor_id, cognoms;RETURNING funciona igual amb UPDATE i DELETE, i pot retornar qualsevol expressió, inclòs *. És una extensió de PostgreSQL molt còmoda que evita el clàssic "insereixo i després consulto".
SQLite no té RETURNING fins a la versió 3.35 (2021); en versions anteriors es fa servir la funció last_insert_rowid():
Què comprova el gestor a cada INSERT
Abans d'acceptar la fila, el gestor verifica les tres regles d'integritat de la lliçó 02-01:
ERROR: duplicate key value violates unique constraint "uq_sucursals_nom"
DETAIL: Key (nom)=(Nord) already exists.INSERT INTO socis (nom, cognoms, data_alta, sucursal_id, actiu)
VALUES ('Prova', 'Prova', '2026-01-01', 99, TRUE);ERROR: insert or update on table "socis" violates foreign key constraint "fk_socis_sucursal"
DETAIL: Key (sucursal_id)=(99) is not present in table "sucursals".Això és exactament el que el full de càlcul de BiblioRed no feia. Aquí l'error no és una molèstia: és el sistema fent la seva feina.
- Lliurable: el joc de dades de BiblioRed
Aquest és l'script de càrrega. Executa'l sencer i en aquest ordre (les filles necessiten les pares) i guarda'l com a dades_biblioredb.sql.
Un matís sobre els identificadors que hi veuràs:
- A
sucursals,autors,exemplars,prestecsireservesdeixem que el gestor els generi. - A
socisillibresels imposem: BiblioRed està migrant des del full de càlcul i vol conservar els números de carnet (Marta Alsina és la sòcia 14 des del 2021) i les signatures del catàleg antic (331 és "El mapa del temps"). És un cas realíssim, i a l'apartat 3 veurem l'efecte secundari que provoca.
-- ============================================================
-- BiblioRed - Joc de dades de prova (tots ficticis)
-- Mòdul 2, lliçó 02-03. Dialecte: PostgreSQL
-- ============================================================
-- PAS 0: partir de zero. Els exemples de l'apartat anterior ja
-- van inserir dues sucursals i un autor; els esborrem perquè
-- l'script sigui l'única font de dades. L'ordre és l'invers al
-- de dependències: primer les filles, després les pares.
DELETE FROM reserves;
DELETE FROM prestecs;
DELETE FROM exemplars;
DELETE FROM llibres;
DELETE FROM socis;
DELETE FROM autors;
DELETE FROM sucursals;
ALTER TABLE sucursals ALTER COLUMN sucursal_id RESTART WITH 1;
ALTER TABLE autors ALTER COLUMN autor_id RESTART WITH 1;
ALTER TABLE exemplars ALTER COLUMN exemplar_id RESTART WITH 1;
ALTER TABLE prestecs ALTER COLUMN prestec_id RESTART WITH 1;
ALTER TABLE reserves ALTER COLUMN reserva_id RESTART WITH 1;
-- SUCURSALS: les quatre biblioteques de la xarxa de Vallmar.
-- Identificadors 1..4 generats pel gestor: Centre=1, Nord=2, Sud=3, Est=4.
INSERT INTO sucursals (nom, adreca, telefon, data_obertura) VALUES
('Centre', 'Plaça Major 1', '900 100 001', '1998-04-12'),
('Nord', 'Avinguda del Parc 45', '900 100 002', '2005-09-30'),
('Sud', 'Carrer Olivar 12', '900 100 003', '2011-02-18'),
('Est', 'Ronda del Port 8', NULL, '2019-06-25');
-- AUTORS: identificadors 1..8 generats pel gestor.
-- Marina Escolà (8) encara no té cap obra al fons:
-- ens servirà per practicar els LEFT JOIN de la propera lliçó.
INSERT INTO autors (nom, cognoms, nacionalitat, any_naixement) VALUES
('Félix J.', 'Palma', 'espanyola', 1968),
('Ken', 'Follett', 'britànica', 1949),
('Irene', 'Valcárcel', 'espanyola', 1975),
('Óscar', 'Barreda', 'espanyola', 1981),
('Nadia', 'Sorrentino', 'italiana', 1970),
('Hugo', 'Lemos', 'portuguesa', 1958),
('Clara', 'Ordóñez', 'espanyola', 1988),
('Marina', 'Escolà', 'espanyola', 1992);
-- SOCIS: números de carnet heretats del full de càlcul (11..20).
-- Pau Miralles (19) no va facilitar correu: el seu email queda a NULL.
-- Ramón Etxebarri (13) està de baixa: actiu = FALSE.
INSERT INTO socis (soci_id, nom, cognoms, email, data_alta, sucursal_id, actiu) VALUES
(11, 'Álvaro', 'Ferran', '[email protected]', '2018-01-22', 1, TRUE),
(12, 'Sonia', 'Quiroga', '[email protected]', '2019-05-03', 3, TRUE),
(13, 'Ramón', 'Etxebarri', '[email protected]', '2020-11-14', 1, FALSE),
(14, 'Marta', 'Alsina', '[email protected]', '2021-03-08', 2, TRUE),
(15, 'Ivan', 'Pereda', '[email protected]', '2021-09-19', 2, TRUE),
(16, 'Núria', 'Bastos', '[email protected]', '2022-01-30', 1, TRUE),
(17, 'Diego', 'Salom', '[email protected]', '2023-02-11', 3, TRUE),
(18, 'Lucía', 'Vendrell', '[email protected]', '2023-07-05', 4, TRUE),
(19, 'Pau', 'Miralles', NULL, '2024-04-16', 2, TRUE),
(20, 'Elena', 'Roig', '[email protected]', '2024-10-01', 1, TRUE);
-- LLIBRES: signatures heretades (331..339).
-- El 339 és una publicació municipal antiga: SENSE ISBN i SENSE autor
-- catalogat. És la prova vivent de per què l'ISBN no podia ser
-- clau primària (lliçó 02-01).
INSERT INTO llibres (llibre_id, isbn, titol, autor_id, editorial, any_publicacio, idioma) VALUES
(331, '9788401339097', 'El mapa del temps', 1, 'Editorial Andana', 2008, 'es'),
(332, '9788401337208', 'Els pilars de la Terra', 2, 'Editorial Andana', 1989, 'es'),
(333, '9788412007701', 'La casa de les marees', 3, 'Edicions Marlia', 2015, 'es'),
(334, '9788412007702', 'Àlgebra per a impacients', 4, 'Premsa Tècnica Nord', 2019, 'es'),
(335, '9788412007703', 'Quaderns de Ravenna', 5, 'Edicions Marlia', 2012, 'es'),
(336, '9788412007704', 'L''hivern dels ocells', 6, 'Editorial Andana', 2021, 'es'),
(337, '9788412007705', 'Rutes del delta', 7, 'Edicions Marlia', 2017, 'ca'),
(338, '9788412007706', 'Manual de jardineria urbana', 4, 'Premsa Tècnica Nord', 2023, 'es'),
(339, NULL, 'Memòria de l''Eixample (1904)', NULL, 'Ajuntament de Vallmar', 1904, 'es');
-- EXEMPLARS: els objectes físics. Identificadors 1..15 generats
-- en l'ordre d'aquesta llista; EJ-3081 serà l'exemplar_id 1.
INSERT INTO exemplars (codi, llibre_id, sucursal_id, estat, data_adquisicio) VALUES
('EJ-3081', 331, 2, 'prestat', '2019-03-14'),
('EJ-3082', 331, 1, 'disponible', '2019-03-14'),
('EJ-3083', 331, 3, 'disponible', '2021-06-01'),
('EJ-3084', 332, 1, 'prestat', '2015-11-20'),
('EJ-3085', 332, 2, 'disponible', '2015-11-20'),
('EJ-3086', 333, 1, 'prestat', '2016-02-09'),
('EJ-3087', 334, 2, 'disponible', '2020-01-15'),
('EJ-3088', 334, 4, 'reparacio', '2020-01-15'),
('EJ-3089', 335, 3, 'disponible', '2013-05-04'),
('EJ-3090', 336, 2, 'prestat', '2022-04-27'),
('EJ-3091', 336, 1, 'disponible', '2022-04-27'),
('EJ-3092', 337, 4, 'disponible', '2018-10-02'),
('EJ-3093', 338, 1, 'disponible', '2023-09-11'),
('EJ-3094', 338, 3, 'baixa', '2023-09-11'),
('EJ-3095', 339, 1, 'disponible', '2003-01-15');
-- PRESTECS: vuit tancats i quatre oberts.
-- data_devolucio NULL = préstec encara en curs.
-- Els quatre préstecs oberts corresponen als quatre exemplars
-- l'estat dels quals és 'prestat' (1, 4, 6 i 10). Coherència total.
INSERT INTO prestecs
(soci_id, exemplar_id, data_prestec, data_devolucio_prevista, data_devolucio, recarrec) VALUES
(14, 2, '2026-03-02', '2026-03-23', '2026-03-19', 0.00),
(15, 1, '2026-03-05', '2026-03-26', '2026-04-02', 1.40),
(16, 5, '2026-03-11', '2026-04-01', '2026-03-30', 0.00),
(14, 7, '2026-04-06', '2026-04-27', '2026-04-25', 0.00),
(18, 9, '2026-04-12', '2026-05-03', '2026-05-10', 1.40),
(16, 4, '2026-05-04', '2026-05-25', '2026-05-22', 0.00),
(11, 1, '2026-05-08', '2026-05-29', '2026-05-27', 0.00),
(12, 12, '2026-05-19', '2026-06-09', '2026-06-30', 4.20),
(14, 1, '2026-07-14', '2026-08-04', NULL, NULL),
(15, 4, '2026-07-18', '2026-08-08', NULL, NULL),
(17, 6, '2026-07-21', '2026-08-11', NULL, NULL),
(19, 10, '2026-07-25', '2026-08-15', NULL, NULL);
-- RESERVES: apunten al LLIBRE (l'obra), no a l'exemplar.
INSERT INTO reserves (soci_id, llibre_id, data_reserva, data_expiracio, estat) VALUES
(16, 331, '2026-07-20', '2026-08-10', 'activa'),
(18, 332, '2026-07-22', '2026-08-12', 'activa'),
(14, 336, '2026-06-30', '2026-07-20', 'atesa'),
(11, 333, '2026-07-05', '2026-07-25', 'cancellada'),
(15, 331, '2026-07-28', '2026-08-18', 'activa');Comprovació de la càrrega
SELECT 'sucursals' AS taula, COUNT(*) AS files FROM sucursals
UNION ALL SELECT 'autors', COUNT(*) FROM autors
UNION ALL SELECT 'socis', COUNT(*) FROM socis
UNION ALL SELECT 'llibres', COUNT(*) FROM llibres
UNION ALL SELECT 'exemplars', COUNT(*) FROM exemplars
UNION ALL SELECT 'prestecs', COUNT(*) FROM prestecs
UNION ALL SELECT 'reserves', COUNT(*) FROM reserves;(COUNT i UNION ALL són de les lliçons 02-05 i 02-04; aquí només ho fem servir com a recompte de control.)
| taula | files |
|---|---|
| sucursals | 4 |
| autors | 8 |
| socis | 10 |
| llibres | 9 |
| exemplars | 15 |
| prestecs | 12 |
| reserves | 5 |
I la correspondència entre codis i identificadors d'exemplar, que necessitaràs per entendre els préstecs:
| exemplar_id | codi | llibre_id |
|---|---|---|
| 1 | EJ-3081 | 331 |
| 2 | EJ-3082 | 331 |
| 3 | EJ-3083 | 331 |
| 4 | EJ-3084 | 332 |
Si els teus recomptes coincideixen, tens el mateix joc de dades que la resta del curs.
Notes per a SQLite
Tres canvis a l'script:
TRUE/FALSEasocis.actiu→1/0.- Les línies
ALTER TABLE ... RESTART WITHdel pas 0 no existeixen a SQLite: elimina-les. AmbINTEGER PRIMARY KEYsenseAUTOINCREMENT, el següent identificador es calcula sol a partir del màxim existent. - Les dates s'escriuen igual (
'2026-03-02'), però es guarden com a text. Mentre facis servir el format ISO, les comparacions i ordenacions continuaran funcionant, perquè l'ordre alfabètic d'AAAA-MM-DDcoincideix amb el cronològic. És exactament la raó per la qual aquest format és el bo.
- Reajustar els generadors d'identificadors
Aquí arriba l'efecte secundari promès. A socis i llibres vam inserir identificadors explícits, però el generador de la columna no se n'ha assabentat: continua apuntant a l'1. Comprovem-ho donant d'alta un soci sense indicar-ne l'identificador:
INSERT INTO socis (nom, cognoms, data_alta, sucursal_id, actiu)
VALUES ('Soci', 'Deprova', '2026-08-01', 1, TRUE)
RETURNING soci_id;El nou soci ha rebut l'1, quan els carnets de BiblioRed comencen a l'11. No ha fallat res, però el desajust ja està sembrat: els deu següents que es donin d'alta s'endurien el 2, el 3… i l'onzè xocaria:
ERROR: duplicate key value violates unique constraint "pk_socis"
DETAIL: Key (soci_id)=(11) already exists.És un dels errors més desconcertants per a qui comença, perquè apareix setmanes després de la càrrega i sense relació aparent amb ella. La causa sempre és la mateixa: s'han inserit claus a mà sense resincronitzar el generador. Esborrem el soci de prova i arreglem-ho:
La solució a PostgreSQL:
ALTER TABLE socis ALTER COLUMN soci_id RESTART WITH 21;
ALTER TABLE llibres ALTER COLUMN llibre_id RESTART WITH 340;O, de manera automàtica i sense haver de mirar el màxim a ull:
A SQLite el problema no existeix si la clau es va declarar com a INTEGER PRIMARY KEY sense AUTOINCREMENT: el valor següent es calcula com el màxim actual més un, així que s'ajusta sol.
SELECT: projecció, àlies i DISTINCT
SELECT: projecció, àlies i DISTINCTSELECT és la instrucció més utilitzada de SQL i la que més lliçons ocupa en aquest curs. La seva forma mínima:
Projecció: triar columnes
| nom | cognoms | data_alta |
|---|---|---|
| Álvaro | Ferran | 2018-01-22 |
| Sonia | Quiroga | 2019-05-03 |
| Ramón | Etxebarri | 2020-11-14 |
| Marta | Alsina | 2021-03-08 |
| Ivan | Pereda | 2021-09-19 |
| Núria | Bastos | 2022-01-30 |
| Diego | Salom | 2023-02-11 |
| Lucía | Vendrell | 2023-07-05 |
| Pau | Miralles | 2024-04-16 |
| Elena | Roig | 2024-10-01 |
Això és la projecció π de l'àlgebra relacional. Recorda que l'ordre en què apareixen les files no està garantit sense ORDER BY: aquí surten en ordre d'inserció perquè la taula és petita i acabada de carregar, però no hi comptis.
SELECT *: còmode i perillós
| sucursal_id | nom | adreca | telefon | data_obertura |
|---|---|---|---|---|
| 1 | Centre | Plaça Major 1 | 900 100 001 | 1998-04-12 |
| 2 | Nord | Avinguda del Parc 45 | 900 100 002 | 2005-09-30 |
| 3 | Sud | Carrer Olivar 12 | 900 100 003 | 2011-02-18 |
| 4 | Est | Ronda del Port 8 | (NULL) | 2019-06-25 |
* significa "totes les columnes". Va perfecte per explorar a la consola, però no el facis servir en codi d'aplicació: portes dades que no necessites per la xarxa, i si demà algú afegeix una columna, el teu programa rep alguna cosa que no esperava. En un script guardat, columnes explícites.
Observa que el NULL del telèfon d'Est apareix com a cel·la buida a psql. El pots fer visible:
Àlies amb AS
Un àlies reanomena una columna al resultat, sense tocar la taula. És l'operador de reanomenament ρ de l'àlgebra relacional.
SELECT codi AS etiqueta,
estat AS situacio,
data_adquisicio AS "data de compra"
FROM exemplars
WHERE sucursal_id = 4;| etiqueta | situacio | data de compra |
|---|---|---|
| EJ-3088 | reparacio | 2020-01-15 |
| EJ-3092 | disponible | 2018-10-02 |
Punts a retenir:
ASés opcional (codi etiquetafunciona igual), però escriure'l fa el SQL molt més llegible.- Perquè un àlies porti espais, majúscules o accents cal posar-lo entre cometes dobles: és un identificador. Aquí sí que és acceptable, perquè l'àlies només viu a la sortida.
També es poden calcular columnes noves:
| nom_complet | data_alta |
|---|---|
| Marta Alsina | 2021-03-08 |
| Ivan Pereda | 2021-09-19 |
| Pau Miralles | 2024-04-16 |
|| és l'operador estàndard de concatenació (funciona a PostgreSQL i SQLite; MySQL fa servir CONCAT()).
Compte amb NULL a les concatenacions: 'Pau' || NULL dóna NULL, no 'Pau'. Si cognoms pogués ser nul, el nom complet desapareixeria sencer. A 02-05 veurem COALESCE, que resol exactament això.
DISTINCT: eliminar duplicats
Retorna 15 files amb moltes repeticions. Amb DISTINCT:
| estat |
|---|
| baixa |
| disponible |
| prestat |
| reparacio |
DISTINCT s'aplica a la combinació completa de columnes seleccionades, no a la primera:
| sucursal_id | estat |
|---|---|
| 1 | disponible |
| 1 | prestat |
| 2 | disponible |
| 2 | prestat |
| 3 | baixa |
| 3 | disponible |
| 4 | disponible |
| 4 | reparacio |
Són les vuit combinacions diferents que existeixen, de les 15 files originals.
Recorda de la lliçó 02-01: a l'àlgebra relacional la projecció sempre elimina duplicats; en SQL cal demanar-ho. DISTINCT obliga el gestor a ordenar o a construir una taula de dispersió, així que té cost: no el posis "per si de cas".
WHERE: filtrar files
WHERE: filtrar filesWHERE és la selecció σ de l'àlgebra: es queda amb les files la condició de les quals sigui CERTA (recorda: DESCONEGUT no passa).
Operadors de comparació
| Operador | Significat |
|---|---|
= |
Igual |
<> o != |
Diferent |
<, >, <=, >= |
Menor, major, menor o igual, major o igual |
| titol | any_publicacio |
|---|---|
| Rutes del delta | 2017 |
| Àlgebra per a impacients | 2019 |
| L'hivern dels ocells | 2021 |
| Manual de jardineria urbana | 2023 |
Les comparacions funcionen també sobre text (ordre alfabètic segons la configuració regional) i sobre dates:
| nom | cognoms | data_alta |
|---|---|---|
| Diego | Salom | 2023-02-11 |
| Lucía | Vendrell | 2023-07-05 |
| Pau | Miralles | 2024-04-16 |
| Elena | Roig | 2024-10-01 |
Operadors lògics: AND, OR, NOT
| codi | estat | sucursal_id |
|---|---|---|
| EJ-3082 | disponible | 1 |
| EJ-3091 | disponible | 1 |
| EJ-3093 | disponible | 1 |
| EJ-3095 | disponible | 1 |
AND té més precedència que OR, igual que la multiplicació sobre la suma. Això provoca errors silenciosos:
-- El que es volia: els exemplars de les sucursals 1 o 2 que estiguin prestats
-- El que fa: els de la sucursal 1 (en qualsevol estat)
-- MÉS els de la 2 que estiguin prestats
SELECT codi, sucursal_id, estat FROM exemplars
WHERE sucursal_id = 1 OR sucursal_id = 2 AND estat = 'prestat';Retorna 7 files: els sis de la sucursal 1 més EJ-3081. Amb parèntesis:
SELECT codi, sucursal_id, estat FROM exemplars
WHERE (sucursal_id = 1 OR sucursal_id = 2) AND estat = 'prestat';| codi | sucursal_id | estat |
|---|---|---|
| EJ-3081 | 2 | prestat |
| EJ-3084 | 1 | prestat |
| EJ-3086 | 1 | prestat |
Consell: fes servir parèntesis sempre que barregis AND i OR, encara que sàpigues la precedència. Qui llegeixi la teva consulta d'aquí a un any t'ho agrairà.
BETWEEN: rangs
SELECT titol, any_publicacio
FROM llibres
WHERE any_publicacio BETWEEN 2010 AND 2019
ORDER BY any_publicacio;| titol | any_publicacio |
|---|---|
| Quaderns de Ravenna | 2012 |
| La casa de les marees | 2015 |
| Rutes del delta | 2017 |
| Àlgebra per a impacients | 2019 |
BETWEEN a AND b és sucre sintàctic de >= a AND <= b: tots dos extrems hi són inclosos. És la font d'un error clàssic amb dates i hores: BETWEEN '2026-03-01' AND '2026-03-31' sobre una columna TIMESTAMP deixa fora tot el que va passar el dia 31 després de mitjanit, perquè 2026-03-31 09:00 és més gran que 2026-03-31 00:00. Amb columnes DATE com les de BiblioRed no hi ha problema.
IN: pertinença a una llista
| codi | estat |
|---|---|
| EJ-3088 | reparacio |
| EJ-3094 | baixa |
IN equival a una cadena d'OR, però és molt més llegible. També existeix NOT IN:
Mateix resultat que abans.
Avís important sobre
NOT INiNULL: si la llista conté unNULL,NOT INno retorna cap fila, per la lògica de tres valors de la lliçó 02-01.x NOT IN (1, 2, NULL)equival ax <> 1 AND x <> 2 AND x <> NULL, i aquest últim terme és sempre DESCONEGUT. Amb llistes escrites a mà no passa; amb llistes que vénen d'una subconsulta (lliçó 02-04) és un parany habitual.
LIKE i ILIKE: cerca de patrons en text
Dos comodins:
| Comodí | Significa |
|---|---|
% |
Zero o més caràcters qualssevol |
_ |
Exactament un caràcter qualsevol |
| titol |
|---|
| El mapa del temps |
| L'hivern dels ocells |
| Rutes del delta |
LIKE distingeix majúscules i minúscules a PostgreSQL:
Cap títol no comença per el en minúscula. Per ignorar la caixa, PostgreSQL ofereix ILIKE (la I és d'insensitive), que no és estàndard però és comodíssim:
| titol |
|---|
| El mapa del temps |
| Els pilars de la Terra |
Alternativa portable, vàlida també a SQLite:
A SQLite, LIKE ja és insensible a majúscules per als caràcters ASCII (però no per a les vocals accentuades), i no existeix ILIKE. És una de les diferències que més sorprenen en portar consultes.
Exemple amb _:
| codi |
|---|
| EJ-3080…EJ-3089 → els deu de la primera desena |
Concretament retorna EJ-3081 a EJ-3089: nou files, perquè EJ-3080 no existeix.
IS NULL: l'absència de valor
Aquí tornem al parany que vam anunciar a 02-01.
| soci_id | nom | cognoms |
|---|---|---|
| 19 | Pau | Miralles |
I el seu complementari, que a BiblioRed té un significat molt concret:
-- Els préstecs encara oberts: no hi ha data de devolució
SELECT prestec_id, soci_id, exemplar_id, data_devolucio_prevista
FROM prestecs
WHERE data_devolucio IS NULL
ORDER BY data_devolucio_prevista;| prestec_id | soci_id | exemplar_id | data_devolucio_prevista |
|---|---|---|---|
| 9 | 14 | 1 | 2026-08-04 |
| 10 | 15 | 4 | 2026-08-08 |
| 11 | 17 | 6 | 2026-08-11 |
| 12 | 19 | 10 | 2026-08-15 |
Quatre préstecs oberts, que coincideixen exactament amb els quatre exemplars en estat prestat. La base de dades és coherent.
I una comprovació de l'altre parany dels NULL, el de les desigualtats:
SELECT COUNT(*) FROM prestecs WHERE recarrec <> 0; -- 3
SELECT COUNT(*) FROM prestecs WHERE recarrec = 0; -- 5
-- 3 + 5 = 8, no 12: falten els quatre préstecs amb recàrrec NULLCap de les dues consultes no veu els NULL. Si vols els préstecs "sense recàrrec pendent", ho has de dir: WHERE recarrec = 0 OR recarrec IS NULL.
ORDER BY: ordenar el resultat
ORDER BY: ordenar el resultatCom que una relació és un conjunt i no té ordre, ORDER BY és l'única manera de garantir-lo.
| titol | any_publicacio |
|---|---|
| Manual de jardineria urbana | 2023 |
| L'hivern dels ocells | 2021 |
| Àlgebra per a impacients | 2019 |
| Rutes del delta | 2017 |
| La casa de les marees | 2015 |
| Quaderns de Ravenna | 2012 |
| El mapa del temps | 2008 |
| Els pilars de la Terra | 1989 |
| Memòria de l'Eixample (1904) | 1904 |
ASC (ascendent) és el valor per defecte; DESC inverteix.
Diversos criteris
S'ordena pel primer i, dins dels empats, pel segon:
| sucursal_id | cognoms | nom |
|---|---|---|
| 1 | Bastos | Núria |
| 1 | Etxebarri | Ramón |
| 1 | Ferran | Álvaro |
| 1 | Roig | Elena |
| 2 | Alsina | Marta |
| 2 | Miralles | Pau |
| 2 | Pereda | Ivan |
| 3 | Quiroga | Sonia |
| 3 | Salom | Diego |
| 4 | Vendrell | Lucía |
Cada criteri porta el seu propi ASC/DESC: ORDER BY sucursal_id ASC, data_alta DESC és perfectament vàlid.
NULLS FIRST i NULLS LAST
On va un NULL en ordenar? L'estàndard deixa llibertat, i PostgreSQL els col·loca al final en ASC i al principi en DESC (equival a tractar-los com el valor més gran). SQLite fa el contrari: els posa al principi en ASC.
Com que no volem dependre del gestor, s'especifica:
SELECT prestec_id, data_devolucio
FROM prestecs
ORDER BY data_devolucio DESC NULLS LAST, prestec_id;| prestec_id | data_devolucio |
|---|---|
| 8 | 2026-06-30 |
| 7 | 2026-05-27 |
| 6 | 2026-05-22 |
| 5 | 2026-05-10 |
| 4 | 2026-04-25 |
| 2 | 2026-04-02 |
| 3 | 2026-03-30 |
| 1 | 2026-03-19 |
| 9 | (NULL) |
| 10 | (NULL) |
| 11 | (NULL) |
| 12 | (NULL) |
Fixa't en el segon criteri, prestec_id: sense ell, l'ordre entre els quatre NULL seria arbitrari. Quan l'ordre importi de veritat, acaba sempre amb un criteri que desempati sense ambigüitat, típicament la clau primària.
NULLS FIRST/NULLS LAST és sintaxi estàndard i funciona a PostgreSQL; SQLite l'admet des de la versió 3.30.
Ordenar per àlies o per posició
SELECT nom || ' ' || cognoms AS nom_complet FROM socis ORDER BY nom_complet;
SELECT nom, cognoms FROM socis ORDER BY 2; -- per la 2a columna: cognomsOrdenar per àlies és legítim i llegible. Ordenar per número de posició funciona, però és fràgil: si algú reordena la llista del SELECT, la consulta canvia de sentit en silenci. Evita-ho.
Ordenació de text i accents
ORDER BY titol situa "Àlgebra per a impacients" en primer lloc si la base de dades fa servir una configuració regional catalana, perquè À s'ordena al costat de la A. Amb la configuració C (byte a byte), À aniria després de la Z. No és una errada: és la col·lació. Pots veure la teva amb SHOW lc_collate; a PostgreSQL.
LIMIT i OFFSET: paginació
LIMIT i OFFSET: paginació| titol |
|---|
| Àlgebra per a impacients |
| El mapa del temps |
| Els pilars de la Terra |
OFFSET salta files abans de començar a comptar:
| titol |
|---|
| La casa de les marees |
| L'hivern dels ocells |
| Manual de jardineria urbana |
Aquesta és la mecànica de la paginació de qualsevol catàleg web: la pàgina N s'obté amb LIMIT mida OFFSET (N-1) * mida.
Dos advertiments:
LIMITsenseORDER BYno té sentit. "Dóna'm 3 files de les 9" sense dir quines significa "dóna'm 3 de qualssevol", i poden ser diferents a cada execució. Encara pitjor: si pagines sense ordenar, la mateixa fila pot sortir a la pàgina 1 i a la 3, i una altra no sortir mai.- Un
OFFSETgran és lent. Per arribar a la fila 100.000 el gestor ha de produir i descartar les 100.000 anteriors. En catàlegs grans es fan servir tècniques de paginació per clau; és tema de rendiment (lliçó 06-03).
La sintaxi estàndard és OFFSET 3 ROWS FETCH FIRST 3 ROWS ONLY, més verbosa i menys utilitzada. LIMIT/OFFSET funciona a PostgreSQL, SQLite i MySQL.
UPDATE: modificar files existents
UPDATE: modificar files existentsAbans de començar: els exemples d'aquest apartat i del següent modifiquen el joc de dades que acabem de carregar, i les lliçons 02-04 i 02-05 el donen per bo. Al final de cada exemple hi incloem la instrucció que desfà el canvi. Executa-la.
L'hàbit que salva bases de dades
Abans de qualsevol UPDATE o DELETE, executa la mateixa condició amb un SELECT. Si el SELECT retorna les files que esperaves, la modificació també les tocarà a elles.
| soci_id | nom | cognoms | |
|---|---|---|---|
| 19 | Pau | Miralles | (NULL) |
-- Pas 2: ara sí, modificar
UPDATE socis SET email = '[email protected]' WHERE soci_id = 19;UPDATE 1 confirma que s'ha tocat exactament una fila. Si veus un número més gran de l'esperat, alguna cosa ha anat malament, i a PostgreSQL, si ets dins d'una transacció, encara hi ets a temps (lliçó 06-01).
-- Pas 3: desfer, perquè el joc de dades continuï com estava
UPDATE socis SET email = NULL WHERE soci_id = 19;Actualitzar diverses columnes i fer servir el valor anterior
Un UPDATE pot calcular el valor nou a partir de l'actual:
SELECT prestec_id, recarrec FROM prestecs WHERE prestec_id = 8; -- 4.20
UPDATE prestecs
SET recarrec = recarrec + 0.50
WHERE prestec_id = 8;| prestec_id | recarrec |
|---|---|
| 8 | 4.70 |
Important: recarrec + 0.50 sobre un NULL dóna NULL. Si haguéssim executat aquest UPDATE sense WHERE, els quatre préstecs oberts haurien passat de NULL a… NULL, i els altres vuit haurien pujat de preu. Silenciosament.
Un cas realista amb dues instruccions
Quan la Marta Alsina retorna EJ-3081, cal tocar dues taules:
-- 1) Tancar el préstec
UPDATE prestecs
SET data_devolucio = '2026-08-01', recarrec = 0.00
WHERE prestec_id = 9;
-- 2) Alliberar l'exemplar
UPDATE exemplars
SET estat = 'disponible'
WHERE codi = 'EJ-3081';Això planteja una pregunta incòmoda: què passa si la primera instrucció funciona i la segona falla? La base quedaria en un estat inconsistent: un préstec tancat i un exemplar marcat com a prestat. La resposta és la transacció, i és el contingut de la lliçó 06-01. De moment, desfem:
UPDATE prestecs SET data_devolucio = NULL, recarrec = NULL WHERE prestec_id = 9;
UPDATE exemplars SET estat = 'prestat' WHERE codi = 'EJ-3081';El WHERE oblidat
-- CATASTRÒFIC: posa el mateix correu als deu socis
UPDATE socis SET email = '[email protected]';I a més violaria uq_socis_email, així que en aquest cas concret la restricció ens salvaria. No sempre hi haurà una restricció que et salvi. Un UPDATE exemplars SET estat = 'baixa'; hauria donat de baixa els 40.000 exemplars de BiblioRed sense protestar.
Costums que eviten el desastre:
- Escriure el
WHEREabans que elSET. Comença teclejantUPDATE taula WHERE ...i després torna a inserir-hi elSET. Sona estrany, funciona. - Provar amb
SELECTprimer. Sempre. - Treballar dins d'una transacció en les operacions delicades (06-01).
- A
psql, activar\set ON_ERROR_STOP onals scripts perquè s'aturin al primer error.
DELETE: eliminar files
DELETE: eliminar filesCrearem una fila d'usar i llençar per no espatllar el joc de dades:
-- Alta de prova
INSERT INTO socis (soci_id, nom, cognoms, email, data_alta, sucursal_id, actiu)
VALUES (99, 'Bruno', 'Temporal', '[email protected]', '2026-08-01', 1, TRUE);| soci_id | nom | cognoms |
|---|---|---|
| 99 | Bruno | Temporal |
A PostgreSQL pots fer servir RETURNING també aquí, per deixar constància del que s'ha endut per davant:
DELETE i la integritat referencial
Intenta esborrar un soci que té préstecs:
ERROR: update or delete on table "socis" violates foreign key constraint
"fk_prestecs_soci" on table "prestecs"
DETAIL: Key (soci_id)=(14) is still referenced from table "prestecs".El gestor t'està protegint. Si permetés l'esborrament, els tres préstecs de la Marta Alsina quedarien orfes: apuntant a un soci que ja no existeix. Aquest comportament —i les seves alternatives, com l'esborrament en cascada— és el tema íntegre de la lliçó 02-06.
A SQLite, si vas oblidar PRAGMA foreign_keys = ON, aquest DELETE funcionarà i et deixarà tres files òrfenes sense dir res. És exactament l'escenari que la lliçó 02-06 t'ensenyarà a detectar i netejar.
El WHERE oblidat, versió definitiva
Sense WHERE, DELETE buida la taula sencera, sense preguntar i sense paperera. Si et passa fora d'una transacció, l'única sortida és la còpia de seguretat (lliçó 06-04). Els quatre costums de l'apartat anterior valen aquí amb més raó encara.
TRUNCATE: buidar una taula sencera
TRUNCATE: buidar una taula senceraQuan el que vols de veritat és buidar una taula completa, existeix una instrucció específica:
DELETE FROM taula |
TRUNCATE TABLE taula |
|
|---|---|---|
| Subllenguatge | DML | DDL |
Admet WHERE |
Sí | No |
| Velocitat en taules grans | Lenta: esborra fila a fila | Gairebé instantània |
| Registra cada fila esborrada | Sí | No |
| Reinicia el generador d'identificadors | No | Opcionalment (RESTART IDENTITY) |
Es pot desfer amb ROLLBACK |
Sí | A PostgreSQL sí; en altres gestors no |
-- Buidar i reiniciar el comptador, arrossegant les taules dependents
TRUNCATE TABLE prestecs, reserves RESTART IDENTITY;SQLite no té TRUNCATE; fes servir DELETE FROM taula sense WHERE, que internament optimitza.
No executis cap
TRUNCATEsobre el teubiblioredb: perdries el joc de dades que acabes de carregar. Si ja ho has fet, torna a executar l'script de l'apartat 2.
Errors Habituals i Consells
UPDATEoDELETEsenseWHERE. L'error més car que es comet amb SQL. Prova sempre la condició amb unSELECTabans.- Escriure
'NULL'en lloc deNULL. Amb cometes és una cadena de text de quatre lletres; sense cometes, l'absència de valor. Una columna que barreja totes dues coses és una columna arruïnada. - Fer servir
= NULLo<> NULL. Zero files, sense error. SempreIS NULL/IS NOT NULL. - Oblidar que
<> valorexclou elsNULL.WHERE recarrec <> 0no retorna els préstecs amb recàrrec nul. Si els vols, afegeix-hiOR recarrec IS NULL. - Barrejar
ANDiORsense parèntesis.ANDguanya. Posa-hi parèntesis sempre. INSERTsense llista de columnes. Es trenca tan bon punt algú toca l'esquema, de vegades en silenci.- Inserir identificadors explícits i no resincronitzar la seqüència. Provoca errors de clau duplicada molt després, quan ja ningú no recorda la càrrega inicial.
LIMITsenseORDER BY. El resultat no és reproduïble i la paginació pot repetir o ometre files.- Refiar-se que
LIKEdistingeix majúscules. A PostgreSQL sí, a SQLite (ASCII) no, a MySQL depèn de la col·lació. Si necessites certesa,LOWER(columna) LIKE .... - Consell: a
psql,\pset null '(nul)'fa visibles elsNULLi\x onmostra els resultats en vertical, ideal per a files amples. - Consell: guarda tot el SQL en fitxers (
esquema_biblioredb.sql,dades_biblioredb.sql) i executa'ls amb\i fitxer.sqlapsqlo.read fitxer.sqlasqlite3. Poder reconstruir la base en deu segons et donarà llibertat per experimentar sense por.
Exercicis
Tots es resolen sobre una sola taula. Escriu la consulta abans de mirar la solució i compara els resultats.
Exercici 1: Consultes de catàleg
- Els títols i editorials dels llibres publicats abans del 2010, del més antic al més recent.
- Els llibres sense ISBN registrat.
- Les editorials diferents del fons, en ordre alfabètic.
- Els títols que contenen la paraula "mar" en qualsevol posició, sense distingir majúscules.
- Els codis dels exemplars de la sucursal 2 que no estiguin disponibles.
- Els tres llibres més recents del fons.
Exercici 2: Consultes sobre socis i préstecs
- Els socis donats d'alta el 2021 o el 2022, amb nom i cognoms en una sola columna anomenada
soci. - Els socis que no estan actius.
- Els préstecs retornats amb retard (la data real de devolució és posterior a la prevista), ordenats per dies de retard… o, si encara no saps calcular la diferència, simplement per data de préstec.
- Els préstecs oberts la devolució dels quals estava prevista abans del 10 d'agost de 2026.
- La segona pàgina d'un llistat de préstecs ordenat per data de préstec descendent, amb 5 préstecs per pàgina.
- Els préstecs el recàrrec dels quals no és zero, inclosos aquells en què el recàrrec encara no s'ha calculat.
Exercici 3: Modificació segura
Escriu les instruccions per a cada operació, precedides del SELECT de comprovació i seguides de la instrucció que desfà el canvi.
- L'exemplar
EJ-3088surt del taller: passa'l adisponible. - Corregeix l'editorial del llibre 337: passa de "Edicions Marlia" a "Edicions Marlia SL".
- Dóna d'alta una sòcia nova, Berta Colomer (
[email protected]), a la sucursal Sud, amb data d'avui (fes servir'2026-08-02'), activa, deixant que el gestor li assigni l'identificador. Després, esborra-la. - La reserva 4 estava cancel·lada per error: reactiva-la.
Solucions
Solució 1
-- 1
SELECT titol, editorial, any_publicacio
FROM llibres
WHERE any_publicacio < 2010
ORDER BY any_publicacio ASC;| titol | editorial | any_publicacio |
|---|---|---|
| Memòria de l'Eixample (1904) | Ajuntament de Vallmar | 1904 |
| Els pilars de la Terra | Editorial Andana | 1989 |
| El mapa del temps | Editorial Andana | 2008 |
| llibre_id | titol |
|---|---|
| 339 | Memòria de l'Eixample (1904) |
| editorial |
|---|
| Ajuntament de Vallmar |
| Edicions Marlia |
| Editorial Andana |
| Premsa Tècnica Nord |
-- 4 (ILIKE a PostgreSQL; LOWER(titol) LIKE '%mar%' és la forma portable)
SELECT titol FROM llibres WHERE titol ILIKE '%mar%';| titol |
|---|
| El mapa del temps |
| La casa de les marees |
Observa que "El mapa" hi entra per mapa i "les marees" per marees: LIKE busca subcadenes, no paraules senceres.
| codi | estat |
|---|---|
| EJ-3081 | prestat |
| EJ-3090 | prestat |
| titol | any_publicacio |
|---|---|
| Manual de jardineria urbana | 2023 |
| L'hivern dels ocells | 2021 |
| Àlgebra per a impacients | 2019 |
Solució 2
-- 1
SELECT nom || ' ' || cognoms AS soci, data_alta
FROM socis
WHERE data_alta BETWEEN '2021-01-01' AND '2022-12-31'
ORDER BY data_alta;| soci | data_alta |
|---|---|
| Marta Alsina | 2021-03-08 |
| Ivan Pereda | 2021-09-19 |
| Núria Bastos | 2022-01-30 |
| soci_id | nom | cognoms |
|---|---|---|
| 13 | Ramón | Etxebarri |
-- 3
SELECT prestec_id, data_prestec, data_devolucio_prevista, data_devolucio, recarrec
FROM prestecs
WHERE data_devolucio > data_devolucio_prevista
ORDER BY data_prestec;| prestec_id | data_prestec | data_devolucio_prevista | data_devolucio | recarrec |
|---|---|---|---|---|
| 2 | 2026-03-05 | 2026-03-26 | 2026-04-02 | 1.40 |
| 5 | 2026-04-12 | 2026-05-03 | 2026-05-10 | 1.40 |
| 8 | 2026-05-19 | 2026-06-09 | 2026-06-30 | 4.20 |
Cal notar que la condició compara dues columnes de la mateixa fila, cosa perfectament legítima. I que els quatre préstecs oberts no hi apareixen: NULL > data és DESCONEGUT. A PostgreSQL, data_devolucio - data_devolucio_prevista donaria directament els dies de retard (7, 7 i 21).
-- 4
SELECT prestec_id, soci_id, data_devolucio_prevista
FROM prestecs
WHERE data_devolucio IS NULL
AND data_devolucio_prevista < '2026-08-10'
ORDER BY data_devolucio_prevista;| prestec_id | soci_id | data_devolucio_prevista |
|---|---|---|
| 9 | 14 | 2026-08-04 |
| 10 | 15 | 2026-08-08 |
-- 5 Pàgina 2 amb 5 per pàgina: OFFSET (2-1) * 5 = 5
SELECT prestec_id, data_prestec
FROM prestecs
ORDER BY data_prestec DESC, prestec_id DESC
LIMIT 5 OFFSET 5;| prestec_id | data_prestec |
|---|---|
| 7 | 2026-05-08 |
| 4 | 2026-04-06 |
| 3 | 2026-03-11 |
| 2 | 2026-03-05 |
| 1 | 2026-03-02 |
(El segon criteri prestec_id DESC garanteix que la paginació sigui estable encara que hi hagués dates repetides.)
-- 6 La clau: NULL no és "diferent de zero", és "no se sap"
SELECT prestec_id, recarrec
FROM prestecs
WHERE recarrec <> 0 OR recarrec IS NULL
ORDER BY prestec_id;| prestec_id | recarrec |
|---|---|
| 2 | 1.40 |
| 5 | 1.40 |
| 8 | 4.20 |
| 9 | (NULL) |
| 10 | (NULL) |
| 11 | (NULL) |
| 12 | (NULL) |
Sense l'OR recarrec IS NULL hauries obtingut només tres files i hauries perdut de vista els quatre préstecs en curs.
Solució 3
-- 1
SELECT exemplar_id, codi, estat FROM exemplars WHERE codi = 'EJ-3088'; -- reparacio
UPDATE exemplars SET estat = 'disponible' WHERE codi = 'EJ-3088'; -- UPDATE 1
UPDATE exemplars SET estat = 'reparacio' WHERE codi = 'EJ-3088'; -- desfer
-- 2
SELECT llibre_id, titol, editorial FROM llibres WHERE llibre_id = 337;
UPDATE llibres SET editorial = 'Edicions Marlia SL' WHERE llibre_id = 337; -- UPDATE 1
UPDATE llibres SET editorial = 'Edicions Marlia' WHERE llibre_id = 337; -- desfer
-- 3 Sense donar soci_id: el genera la columna IDENTITY (serà el 21 si
-- vas resincronitzar la seqüència a l'apartat 3; si no, donarà error de
-- clau duplicada, que és justament la lliçó d'aquell apartat).
INSERT INTO socis (nom, cognoms, email, data_alta, sucursal_id, actiu)
VALUES ('Berta', 'Colomer', '[email protected]', '2026-08-02', 3, TRUE)
RETURNING soci_id;
-- soci_id
-- ---------
-- 21
SELECT soci_id, nom, cognoms FROM socis WHERE cognoms = 'Colomer';
DELETE FROM socis WHERE cognoms = 'Colomer'; -- DELETE 1
-- 4
SELECT reserva_id, estat FROM reserves WHERE reserva_id = 4; -- cancellada
UPDATE reserves SET estat = 'activa' WHERE reserva_id = 4; -- UPDATE 1
UPDATE reserves SET estat = 'cancellada' WHERE reserva_id = 4; -- desferComprovació final que tot ha quedat com estava:
SELECT COUNT(*) FROM socis; -- 10
SELECT COUNT(*) FROM prestecs; -- 12
SELECT estat FROM reserves WHERE reserva_id = 4; -- cancelladaConclusió
Aquesta lliçó ha portat BiblioRed d'un esquema buit a una base de dades viva i consultable:
INSERTamb llista de columnes explícita (sempre), en una o diverses files, ambNULLsense cometes i ambRETURNINGa PostgreSQL per conèixer els identificadors generats. I la comprovació automàtica d'unicitat i de claus foranes que el full de càlcul mai no va tenir.- El joc de dades de BiblioRed: 4 sucursals, 8 autors, 10 socis, 9 llibres, 15 exemplars, 12 préstecs i 5 reserves, tot fictici i coherent. És el material de treball de la resta del curs.
- L'efecte secundari d'inserir claus a mà i com resincronitzar el generador amb
RESTART WITHosetval. SELECTamb projecció de columnes, àliesAS(amb cometes dobles quan porten espais), concatenació amb||iDISTINCTaplicat a la combinació completa de columnes.WHEREamb comparacions,AND/OR/NOTi la seva precedència,BETWEEN(extrems inclosos),INiNOT IN(amb el seu parany ambNULL),LIKE/ILIKEamb%i_, iIS NULL, que és l'única manera de preguntar per l'absència.ORDER BYamb diversos criteris,ASC/DESC,NULLS FIRST/NULLS LASTi la importància d'un criteri de desempat.LIMIT/OFFSETper paginar, sempre ambORDER BY.UPDATEiDELETEamb la disciplina delSELECTprevi, la lectura del nombre de files afectades i el respecte que imposa elWHEREoblidat; mésTRUNCATEcom a buidatge ràpid de nivell DDL.
Fins aquí, cada consulta ha mirat una sola taula, i això deixa preguntes sense resposta: no sabem qui té EJ-3081, ni quin títol és el més prestat, ni quins socis no han agafat mai res. Les dades estan repartides a propòsit —aquesta és l'essència del model relacional— i ara toca recompondre-les.
A la lliçó 02-04, Consultes Multitaula: JOIN i Subconsultes, aprendràs a unir socis amb prestecs, prestecs amb exemplars, exemplars amb llibres i llibres amb autors, tot en una sola consulta; a fer servir LEFT JOIN per trobar el que no té correspondència; i a niar consultes dins de consultes. És on SQL comença a resultar realment potent.
Fonaments de Bases de Dades
Mòdul 1: Introducció a les Bases de Dades
- Conceptes Bàsics de Bases de Dades
- Tipus de Bases de Dades
- Història i Evolució de les Bases de Dades
- Sistemes Gestors de Bases de Dades i Arquitectura
Mòdul 2: Bases de Dades Relacionals
- Model Relacional
- Llenguatge SQL
- Operacions Bàsiques en SQL
- Consultes Multitaula: JOIN i Subconsultes
- Agregació i Agrupació de Dades
- Integritat Referencial
Mòdul 3: Bases de Dades No Relacionals
- Introducció a NoSQL
- Tipus de Bases de Dades NoSQL
- Modelatge de Dades en NoSQL
- Comparació entre Bases de Dades Relacionals i No Relacionals
Mòdul 4: Disseny d'Esquemes
- Principis de Disseny d'Esquemes
- Diagrames Entitat-Relació (ER)
- Transformació de Diagrames ER a Esquemes Relacionals
- Tipus de Dades i Restriccions
Mòdul 5: Normalització
Mòdul 6: Transaccions, Rendiment i Seguretat
- Transaccions i Propietats ACID
- Concurrència i Nivells d'Aïllament
- Índexs i Optimització de Consultes
- Seguretat, Permisos i Còpies de Seguretat
Mòdul 7: Exercicis Pràctics
- Exercicis de SQL
- Exercicis de Disseny d'Esquemes
- Exercicis de Normalització
- Exercicis de Consultes Avançades i Transaccions
Mòdul 8: Casos d'Estudi
- Cas d'Estudi: Base de Dades Relacional
- Cas d'Estudi: Base de Dades No Relacional
- Cas d'Estudi: Persistència Poliglota
