El correu de l'Helena és el context; això és el contracte. Un requisit serveix d'alguna cosa només si es pot comprovar: "el sistema ha de gestionar bé els préstecs" no és un requisit, és un desig. "Un exemplar no pot aparèixer en dos préstecs amb data_devolucio IS NULL, i la base de dades ha de rebutjar l'intent" sí que ho és, perquè hi ha una manera objectiva de saber si es compleix: intentar-ho i veure si falla.
Aquesta lliçó és aquesta especificació, agrupada en sis blocs —dades, integritat, consulta, rendiment, seguretat i lliurament— i tancada amb la rúbrica amb què es corregeix el projecte, perquè puguis autoavaluar-te abans de donar-lo per acabat. Dins de cada bloc hi ha dues capes: el que no és negociable, perquè és el que el projecte pretén ensenyar, i el que queda al teu criteri, perquè dissenyar és triar i a 12-04 veuràs que diverses solucions diferents són igual de correctes.
Contingut
- Convencions obligatòries
- Requisits de dades (RD)
- Requisits d'integritat (RI)
- Requisits de consulta (RC): les 15 consultes
- Requisits de rendiment (RP)
- Requisits de seguretat (RS)
- Requisits de lliurament (RE)
- Criteris de qualitat
- Rúbrica d'avaluació
- Errors habituals i consells
- Exercicis
- Conclusió
- Convencions obligatòries
Són les del curs, i es donen per sabudes (05-01, 11-02). Es llisten perquè formen part de la correcció:
| Element | Convenció |
|---|---|
| Identificadors | snake_case, en singular per a columnes i plural per a taules, sense accents ni ç |
| Clau primària | id INTEGER GENERATED BY DEFAULT AS IDENTITY (o composta en taules pont pures) |
| Clau forana | <taula_referenciada>_id, sempre amb ON DELETE explícit |
| Restriccions | Amb nom: pk_, fk_, uq_, chk_; índexs idx_ |
| Diners | NUMERIC(10,2). Mai coma flotant |
| Dates | DATE, llevat d'on calgui l'instant |
| Nul·labilitat | NOT NULL per omissió; cada NULL permès és una decisió documentada |
| Estil de consulta | AS explícit en àlies de columna, àlies de taula per inicial, una clàusula per línia |
| Data de referència | DATE '2026-06-30' en lloc de CURRENT_DATE, perquè els resultats siguin reproduïbles |
- Requisits de dades (RD)
Aquestes són les entitats mínimes obligatòries. Pots afegir-hi columnes, i pots afegir-hi taules si les justifiques; el que no pots és treure'n cap ni fusionar-ne dues.
| # | Entitat | Atributs imprescindibles | Notes |
|---|---|---|---|
| RD-01 | seus |
nom (únic), adreça, data d'obertura | 3 files mínim |
| RD-02 | materies |
nom (únic) | Equivalent a categories |
| RD-03 | editorials |
nom (únic), país | Equivalent a proveidors |
| RD-04 | autors |
nom, cognoms, nacionalitat, any de naixement | 8 files mínim |
| RD-05 | obres |
títol, matèria, editorial, any de publicació, ISBN | Sense columna d'existències |
| RD-06 | obres_autors |
obra, autor, rol, ordre de signatura | N:M obligatòria, amb PK composta |
| RD-07 | exemplars |
obra, seu, codi de barres (únic), data d'adquisició, estat físic | Una fila per objecte físic |
| RD-08 | socis |
nom, cognoms, document (únic), data de naixement, tipus, estat, seu d'alta, data d'alta | tipus i estat amb domini tancat |
| RD-09 | bibliotecaris |
nom, cognoms, càrrec, seu, responsable, email (únic) | Jerarquia reflexiva obligatòria |
| RD-10 | prestecs |
exemplar, soci, bibliotecari, data de préstec, data prevista, data real de devolució, renovacions | Una fila per exemplar prestat |
| RD-11 | reserves |
obra, soci, seu de recollida, data de reserva, estat, dates d'avís i tancament | Penja d'obres |
| RD-12 | multes |
préstec (únic), import, dies de retard, data de generació, data de pagament | Taula pròpia, no columna |
El que no és negociable, i per què:
- La separació obra / exemplar (RD-05 + RD-07). És l'objecte del projecte. Una sola taula
llibresamb un comptador és un suspens automàtic, per ben escrit que estigui la resta. - La N:M
obres_autors(RD-06). Una columnaautoraobresno permet dos autors, i unaautor1/autor2és l'antipatró de les columnes repetides (01-05). Aquí sí, PK composta(obra_id, autor_id)senseidpropi: és una taula pont pura i aquella era l'alternativa que 05-01 va descriure. - La jerarquia de bibliotecaris (RD-09).
responsable_idreferenciant la mateixa taula, nul·lable només a la direcció. És el que alimenta elSELF JOINde 03-06 i la CTE recursiva de 10-02. - L'històric que no s'esborra (RD-10). Cap
DELETEsobreprestecs. Les baixes de socis i d'exemplars són lògiques, i les FK cap aprestecshan d'impedir físicament l'esborrat del pare. prestecspenja d'exemplarsireservespenja d'obres. Invertir qualsevol de les dues trenca el model.
El que queda al teu criteri: si editorials és taula o columna de text (es demana taula, però accepta discussió), si obres porta idioma o nombre de pàgines, si socis desa el telèfon, si exemplars desa la signatura topogràfica, si reserves té taula d'estats o CHECK, i com distingeixes un exemplar extraviat d'un donat de baixa.
Volum mínim de dades de prova: 3 seus, 6 matèries, 5 editorials, 8 autors, 10 obres, 15 exemplars, 12 socis, 25 préstecs, 4 reserves i 4 multes. Menys que això no permet comprovar les consultes; molt més, no aporta res a mà. (El joc de dades de referència de 12-03 fa servir 3 / 6 / 5 / 10 / 12 / 20 / 15 / 36 / 5 / 6.)
- Requisits d'integritat (RI)
Totes aquestes regles han d'estar declarades a la base de dades. Que l'aplicació també les comprovi està bé; que només l'aplicació les comprovi és exactament el que 05-01 anomenava "confiar que tothom se'n recordi, de validar".
| # | Regla | Com es declara |
|---|---|---|
| RI-01 | La data prevista és posterior a la de préstec | CHECK (data_prevista > data_prestec) |
| RI-02 | La data de devolució no és anterior a la de préstec | CHECK (data_devolucio IS NULL OR data_devolucio >= data_prestec) |
| RI-03 | Un exemplar no pot estar en dos préstecs actius | El punt difícil. Vegeu més avall |
| RI-04 | Les renovacions estan entre 0 i el màxim (RN-03) | CHECK sobre el rang; el topall per tipus, a la lògica |
| RI-05 | L'import d'una multa mai no és negatiu, i els seus dies de retard són > 0 | Dos CHECK |
| RI-06 | Una multa no es pot pagar abans de generar-se | CHECK (data_pagament IS NULL OR data_pagament >= data_generacio) |
| RI-07 | Un préstec té com a molt una multa | UNIQUE (prestec_id) a multes |
| RI-08 | L'ISBN, si existeix, és únic | UNIQUE (isbn), nul·lable |
| RI-09 | El codi de barres de l'exemplar és únic i obligatori | NOT NULL UNIQUE |
| RI-10 | El tipus i l'estat del soci pertanyen al seu domini | CHECK (... IN (...)) |
| RI-11 | L'estat de l'exemplar i el de la reserva, igual | Dos CHECK |
| RI-12 | Un bibliotecari no pot ser el seu propi responsable | CHECK (responsable_id <> id) |
| RI-13 | Un soci no pot tenir dues reserves vives de la mateixa obra | Índex únic parcial sobre els estats vius |
| RI-14 | No es pot esborrar un soci, un exemplar ni una obra amb historial | ON DELETE RESTRICT a les FK de prestecs |
El punt difícil: RI-03
Aquest requisit es mereix el seu apartat perquè és on es veu si has entès el mòdul 8. La regla és: hi pot haver molts préstecs de l'exemplar 1 —la seva història sencera— però com a molt un amb data_devolucio IS NULL.
Les quatre sortides possibles, i per què tres no valen:
| Intent | Problema |
|---|---|
UNIQUE (exemplar_id) |
Prohibeix l'històric: l'exemplar 1 no es podria prestar dues vegades a la vida |
UNIQUE (exemplar_id, data_devolucio) |
No funciona: NULL no és igual a NULL (04-03, 05-01), així que admet infinits préstecs actius |
Un CHECK amb subconsulta |
Il·legal: un CHECK no pot consultar altres files ni altres taules (05-01) |
Una columna actiu BOOLEAN amb UNIQUE parcial |
Funciona, però duplica informació: actiu i data_devolucio IS NULL dirien el mateix i es podrien contradir |
La solució que demana el projecte és un índex únic parcial (08-02): un índex que només conté les files que compleixen una condició, i que per tant només imposa unicitat entre elles.
CREATE UNIQUE INDEX uq_prestec_actiu_per_exemplar
ON prestecs (exemplar_id)
WHERE data_devolucio IS NULL;Es llegeix literalment: "entre els préstecs sense retornar, l'exemplar_id és únic". És declaratiu, ho fa complir el motor, ocupa només tantes entrades com préstecs vius hi hagi —sis, no centenars de milers— i de propina accelera totes les consultes de préstecs actius. A 12-03 s'implementa i a 12-04 es compara amb l'alternativa de la restricció EXCLUDE.
Nota de dialecte: els índexs parcials són de PostgreSQL, SQLite i (amb matisos) SQL Server, on s'anomenen filtered indexes. MySQL no els té, i allà la solució habitual és una columna generada amb
NULLquan el préstec està tancat, més unUNIQUEsobre ella — perquèUNIQUEignora els nuls.
- Requisits de consulta (RC): les 15 consultes
Aquestes quinze consultes són el lliurable 03-consultes.sql. Estan ordenades per dificultat creixent i cadascuna porta la tècnica que exercita i la lliçó on es va ensenyar, perquè sàpigues on tornar si t'encalles. Totes fan servir DATE '2026-06-30' com a data de referència.
| # | Consulta | Tècnica | Lliçó |
|---|---|---|---|
| RC-01 | Obres publicades des del 2015, amb el seu ISBN, ordenades per any descendent i títol | WHERE, ORDER BY amb desempat |
02-04, 02-06 |
| RC-02 | Exemplars d'una seu donada, amb el títol de la seva obra i la seva matèria | JOIN de quatre taules |
03-02 |
| RC-03 | Préstecs actius: soci, obra, seu, dies fora i dies de retard | JOIN + IS NULL + aritmètica de dates |
03-02, 04-03, 06-03 |
| RC-04 | Els tres buits del sistema: socis sense préstecs, obres sense exemplars i exemplars mai prestats, en un sol resultat | Anti-join + UNION ALL |
03-03, 03-07, 07-03 |
| RC-05 | Matèries amb 5 o més préstecs, amb la seva durada mitjana | GROUP BY + HAVING |
04-05, 04-06 |
| RC-06 | Disponibilitat per obra: exemplars totals, habilitats, prestats ara i disponibles | Agregat condicional amb FILTER |
04-04, 06-05 |
| RC-07 | Obres signades per més d'un autor, amb els seus autors en una sola cel·la i en ordre de signatura | N:M + string_agg |
03-02, 04-04 |
| RC-08 | Multes per tipus de soci: total, cobrat i pendent, amb la taxa de retard | FILTER + COALESCE + mètrica definida |
06-04, 11-04 |
| RC-09 | Socis amb més préstecs que la mitjana del seu tipus | Subconsulta correlacionada | 07-02, 07-03 |
| RC-10 | Cua de reserves en espera, amb la posició de cada soci a la cua de la seva obra | ROW_NUMBER() amb partició |
10-03 |
| RC-11 | Les 3 obres més prestades de cada seu | LATERAL (o finestra filtrada) |
07-04 |
| RC-12 | Préstecs per mes dels darrers 12 mesos, sense buits, amb acumulat i mitjana mòbil de 3 | generate_series + LEFT JOIN + finestres |
11-01, 10-03 |
| RC-13 | Els 3 socis més lectors de cada seu, amb desempat explícit | RANK / ROW_NUMBER en partició |
10-03 |
| RC-14 | Organigrama de bibliotecaris amb el seu nivell i la seva ruta jeràrquica | CTE recursiva | 10-02 |
| RC-15 | Informe pivotat: préstecs per seu (files) i matèria (columnes), sense perdre cap seu | Pivot amb FILTER + LEFT JOIN |
06-05, 11-04 |
Cinc condicions que s'apliquen a les quinze:
- Cada consulta va precedida d'un comentari que digui quina pregunta respon i, si maneja una mètrica, com la defineix (11-04). "Préstecs" inclou els actius? I els de socis de baixa? Escriu-ho.
- Cap consulta pot perdre files per un
JOINmal triat. RC-06, RC-12 i RC-15 han d'ensenyar els zeros: l'obra sense exemplars, el mes sense préstecs i la seu sense res en una matèria. - Tot
ORDER BYd'un rànquing necessita desempat. Sense ell, dues execucions poden retornar ordres diferents i l'informe deixa de ser reproduïble. - Cada consulta lliurada va amb el seu resultat. No n'hi ha prou amb el SQL: cal haver-lo executat.
- Cap consulta fa servir
SELECT *. Columnes explícites, sempre (11-02).
- Requisits de rendiment (RP)
El projecte es prova amb desenes de files, però es dissenya per a desenes de milers de socis i centenars de milers de préstecs. Els índexs es justifiquen amb aquesta mida, no amb la del fitxer de proves.
| # | Requisit |
|---|---|
| RP-01 | Índex a totes les claus foranes que es fan servir per navegar: PostgreSQL indexa la PK, no la FK (08-01). prestecs(exemplar_id), prestecs(soci_id), exemplars(obra_id), exemplars(seu_id), obres(materia_id) |
| RP-02 | L'índex únic parcial de RI-03, que a més resol la consulta de préstecs actius |
| RP-03 | Un índex que serveixi a la consulta de vençuts i a la sèrie temporal: sobre prestecs(data_prestec) i sobre les columnes per les quals es filtra el venciment |
| RP-04 | Un índex per a la cerca per títol, amb la decisió raonada entre B-tree sobre una expressió, pg_trgm o cerca de text complet (08-03) |
| RP-05 | Cada índex es justifica amb la consulta concreta que l'aprofita. Un índex sense consulta que el faci servir és un índex que només frena les escriptures (08-02) |
| RP-06 | L'informe inclou l'EXPLAIN d'almenys dues consultes abans i després de crear el seu índex, amb el canvi de pla comentat (08-05) |
| RP-07 | Es declara què s'ha decidit no indexar i per què |
RP-05 i RP-07 són els que de debò es corregeixen. Posar deu índexs és fàcil; explicar per què aquests deu i no uns altres és el que demostra criteri.
- Requisits de seguretat (RS)
Tres rols, segons el model d'11-03: es concedeixen privilegis a rols de grup i els usuaris se'n fan membres, mai al revés.
| # | Rol | Permisos |
|---|---|---|
| RS-01 | bib_consulta |
SELECT sobre catàleg (obres, exemplars, autors, materies, editorials, seus) i sobre les vistes públiques. Cap accés a socis, prestecs ni multes |
| RS-02 | bib_taulell |
Tot l'anterior, més SELECT/INSERT/UPDATE a prestecs, reserves i multes, i SELECT/UPDATE a socis. Sense DELETE a cap (RD-10) |
| RS-03 | bib_admin |
Tot l'anterior, més DDL i gestió del catàleg. No és el propietari ni un superusuari |
I quatre requisits sobre les dades personals, que aquí no són un adorn: un historial de préstecs és un registre del que llegeix una persona, una de les dades més sensibles que pot desar una biblioteca.
- RS-04. Cap rol d'aplicació té
DELETEsobre l'històric, i cap no és propietari de les taules. - RS-05. Els informes de direcció se serveixen de vistes agregades que no exposen què ha llegit cada soci. Qui necessita la dada agregada no necessita la dada individual.
- RS-06. La baixa d'un soci és lògica; l'anonimització posterior (substituir nom, document i email per valors neutres conservant l'
id) ha d'estar prevista, per poder complir una petició de supressió sense destruir l'estadística. - RS-07. Les dades de prova són íntegrament fictícies: noms inventats, documents amb format vàlid però inexistents i correus a
@example.com, que és un domini reservat precisament per a això. No facis servir mai dades reals, ni tan sols les teves, en un fitxer que acabarà en un repositori.
Opcionalment, i com a exercici d'11-03: implementa RLS perquè un bibliotecari només vegi els socis de la seva seu. Suma a la rúbrica, però no és obligatori i no compensa lliurar-ho malament.
- Requisits de lliurament (RE)
| Fitxer | Contingut | Ha de complir |
|---|---|---|
01-esquema.sql |
DROP en ordre invers, CREATE TABLE en ordre de dependències, restriccions amb nom, índexs, vistes |
Idempotent: executar-lo dues vegades deixa el mateix estat |
02-dades.sql |
Dades de prova, en ordre de dependències, amb setval final si insereixes id explícits |
Executable després de l'esquema, sense errors |
03-consultes.sql |
Les 15 consultes, numerades i comentades | Només lectura: ni un INSERT, ni un UPDATE |
04-informe.md |
L'informe, segons 12-05 | Entre 4 i 8 pàgines |
RE-01. Els fitxers s'executen en ordre: 01, 02, 03. La seqüència completa ha de funcionar d'una tirada sobre una base buida:
createdb biblioteca
psql -d biblioteca -f 01-esquema.sql
psql -d biblioteca -f 02-dades.sql
psql -d biblioteca -f 03-consultes.sqlRE-02. L'esquema és idempotent pel mateix mecanisme que botigaverda.sql (01-06): DROP TABLE IF EXISTS ... CASCADE al principi, en ordre invers a les dependències. És el correcte per a un script de creació des de zero; per a un sistema en producció serien migracions versionades (05-06), i dir-ho a l'informe suma.
RE-03. Els fitxers van en un repositori git amb el seu README.md (12-05), sense credencials de cap mena.
RE-04. Cada consulta de 03-consultes.sql porta a sobre el seu número RC-nn, la pregunta que respon i les lliçons que aplica.
- Criteris de qualitat
Aquests no són requisits amb número: són la diferència entre un projecte que funciona i un que a més està ben fet. Surten íntegres d'11-02.
- Format uniforme. Paraules clau en majúscula, una clàusula per línia, àlies de taula curts i consistents, sagnat estable. Si dues consultes del mateix fitxer es veuen diferents, es nota.
- Noms que no necessiten comentari.
data_devoluciosí;ddno.uq_prestec_actiu_per_exemplarsí;idx3no. - Comentaris que expliquen el perquè, no el què.
-- El LEFT JOIN és obligatori: hi ha obres sense cap exemplarés útil.-- Uneix obres amb exemplarsés soroll. - Ni una consulta sense executar. Un
03-consultes.sqlamb una consulta que dona error és la fallada més cara de totes, perquè costa zero evitar-la. - Ni una definició implícita. Cada mètrica de l'informe diu què inclou i què exclou (11-04).
- Res de sobreenginyeria. Tres disparadors, cinc vistes materialitzades i una taula d'auditoria en un projecte de dotze taules no sumen: resten, perquè cal mantenir-les i defensar-les.
- Rúbrica d'avaluació
Fes-la servir com a llista d'autoavaluació abans de lliurar. La columna de pes indica quant compta cada bloc.
| Criteri | Pes | Què es mira |
|---|---|---|
| Model de dades | 25 % | Separació obra/exemplar; N:M correcta; jerarquia reflexiva; normalització sense excessos; decisions de desnormalització justificades |
| Integritat | 20 % | Les 14 RI declarades i amb nom; RI-03 resolta amb índex parcial; ON DELETE coherent amb la semàntica de cada relació |
| Consultes | 25 % | Les 15 s'executen i són correctes; sense files perdudes ni duplicades; ordenacions amb desempat; mètriques definides |
| Rendiment | 10 % | Índexs justificats un a un amb la seva consulta; dos EXPLAIN comentats; el que es decideix no indexar |
| Seguretat | 10 % | Tres rols amb privilegi mínim; sense DELETE sobre l'històric; dades personals fictícies i baixa lògica |
| Lliurament i informe | 10 % | Els quatre fitxers s'executen en ordre; estil uniforme; informe amb model, decisions, resultats i limitacions |
Quatre errors anul·len el bloc sencer, per molt bé que estigui la resta:
- Una sola taula
llibresen lloc d'obres+exemplars→ model a zero. - RI-03 sense resoldre o resolta només a l'aplicació → integritat a zero.
- Una consulta del lliurable que dona error en executar-se → consultes a zero.
- Dades personals reals al repositori → lliurament a zero.
Errors habituals i consells
- Llegir els requisits una vegada, al principi. Torna a aquesta lliçó en acabar cada bloc i ratlla el que has complert. La meitat del que es perd a la rúbrica és material oblidat, no material mal fet.
- Prendre els mínims com a objectius. "12 socis" és el terra perquè les consultes tinguin sentit, no la meta. Però més d'un parell de centenars de files a mà és temps llençat: per a volum,
generate_series(12-03). - Confondre requisit i solució. RI-03 diu què s'ha de complir; l'índex parcial és com ho resolem aquí. Si trobes una altra manera que també ho garanteixi a la base, és vàlida — i a 12-04 n'hi ha una.
- Resoldre a l'aplicació el que demana la base. "Ja ho comprova el meu codi abans d'inserir" no compleix RI-03. Dues peticions simultànies s'hi colen, i això és exactament el que el mòdul 9 explicava.
- Escriure les quinze consultes d'una tirada i executar-les al final. Executa cadascuna tan bon punt l'escriguis i comprova el recompte de files a cada
JOIN(11-04). Un error a la tercera contamina les dotze següents. - Consell: converteix aquesta lliçó en un fitxer
requisits.mddel repositori, amb caselles de verificació. És la checklist d'11-02 aplicada al teu propi projecte, i és el que consultarà qui et corregeixi. - Consell: escriu primer la consulta més difícil que hi vegis (probablement RC-11 o RC-12). Si el model aguanta la més difícil, aguanta les altres catorze; si no, val més descobrir-ho abans de carregar les dades.
- Consell: desa la sortida de cada consulta en un fitxer. Quan canviïs l'esquema o les dades, podràs comparar i veure què s'ha mogut. És la "xifra de control" d'11-04 aplicada al projecte.
Exercicis
Com a 12-01, són tasques del projecte: porten esbós o rúbrica, no solució completa.
Tasca 1 — El pla d'atac
Converteix els requisits en un pla de treball teu: una taula amb les tasques, la seva dependència amb les altres, l'estimació en hores i el requisit que tanca cadascuna. Ha de cobrir del model al lliurament. Si el teu pla no inclou una tasca explícita de "generar dades de prova amb casos límit", està incomplet.
Tasca 2 — Predir el punt difícil
Abans d'escriure gens de SQL, raona sobre RI-03: (1) per què UNIQUE (exemplar_id, data_devolucio) no funciona a PostgreSQL, i en quin motor sí que funcionaria? (2) Escriu l'INSERT exacte que hauria de fallar i el que ha de continuar funcionant. (3) Què passa amb l'índex parcial si demà es decideix que un exemplar es pot prestar "en sala" alhora que està prestat a domicili?
Tasca 3 — Definir les mètriques
Abans de RC-08, escriu la definició exacta d'aquestes cinc mètriques, dient què inclou i què no, a l'estil de la taula de definicions d'11-04: préstecs del període, soci actiu, taxa de retard, deute pendent i obra disponible. Per a cadascuna, indica a més quina altra definició raonable existeix i com canviaria la xifra.
Solucions
Rúbrica de la Tasca 1
Un pla acceptable té entre 8 i 12 tasques i respecta aquestes dependències: model → DDL → dades → consultes → índexs → vistes i seguretat → informe. Els dos errors de planificació que es repeteixen són deixar les dades de prova per al final —i descobrir llavors que el model no permet representar un cas— i deixar l'informe per a l'últim dia, quan ja no recordes per què vas prendre la meitat de les decisions. Escriu l'informe a mesura que decideixes.
Solució de la Tasca 2
(1) Perquè a l'estàndard SQL, i a PostgreSQL, NULL no és igual a NULL, així que dues files amb (1, NULL) no es consideren duplicades i el UNIQUE les admet totes dues (04-03, 05-01). A SQL Server sí que fallaria, perquè tracta tots els NULL com a iguals a efectes de l'índex únic — i a PostgreSQL 15+ es pot imitar amb UNIQUE NULLS NOT DISTINCT, encara que per a aquest cas l'índex parcial continua sent millor perquè a més indexa només les files vives.
(2) Ha de fallar un segon préstec obert del mateix exemplar, i ha de continuar funcionant un altre préstec tancat del mateix exemplar:
-- ⚠️ INCORRECTA: l'exemplar 1 ja té un préstec sense retornar
INSERT INTO prestecs (exemplar_id, soci_id, bibliotecari_id, data_prestec, data_prevista)
VALUES (1, 13, 5, DATE '2026-06-25', DATE '2026-07-16');
-- ✅ CORRECTA: és història, no un préstec viu
INSERT INTO prestecs (exemplar_id, soci_id, bibliotecari_id, data_prestec, data_prevista, data_devolucio)
VALUES (1, 13, 5, DATE '2024-01-10', DATE '2024-01-31', DATE '2024-01-28');La primera retorna:
ERROR: duplicate key value violates unique constraint "uq_prestec_actiu_per_exemplar" DETAIL: Key (exemplar_id)=(1) already exists.
(3) L'índex deixaria de valer tal qual, perquè ja no hi hauria "com a molt un préstec actiu" sinó "com a molt un de cada modalitat". La solució seria afegir una columna modalitat i incloure-la a l'índex: ON prestecs (exemplar_id, modalitat) WHERE data_devolucio IS NULL. És un bon recordatori que una restricció codifica una regla de negoci concreta, i que quan la regla canvia, la restricció canvia amb ella — la qual cosa, per cert, és un avantatge: si la regla visqués repartida pel codi de l'aplicació, ningú no sabria on tocar.
Esbós de la Tasca 3
Dues de les cinc, per fixar el nivell de detall esperat:
| Mètrica | Definició del projecte | Alternativa raonable |
|---|---|---|
| Préstecs del període | Files de prestecs amb data_prestec dins del període, tots els estats, inclosos els que segueixen oberts i els de socis que després es van donar de baixa |
Comptar només els tancats, per poder parlar de durada mitjana. Dona una xifra menor i no val per mesurar demanda |
| Deute pendent | SUM(import) de multes amb data_pagament IS NULL. No inclou els préstecs vençuts sense retornar, que encara no han generat multa (RN-10) |
Incloure-hi el deute potencial dels vençuts, calculat a dia d'avui. És la xifra que interessa a direcció, i cal anomenar-la d'una altra manera per no barrejar-la amb la comptable |
La lliçó de fons és la d'11-04: no hi ha una definició correcta, hi ha una definició escrita. El greu no és triar malament; és publicar dues xifres diferents al mateix informe sense dir en què es diferencien.
Conclusió
Ja tens el contracte del projecte:
- 12 requisits de dades amb les entitats mínimes i els seus atributs. L'innegociable: la separació obra / exemplar, la N:M
obres_autorsamb PK composta, la jerarquia reflexiva de bibliotecaris, l'històric que no s'esborra, i queprestecspengi d'exemplarsmentrereservespenja d'obres. - 14 requisits d'integritat, tots declarats a la base i amb nom. El difícil és RI-03 —un exemplar, un sol préstec actiu—, que no es pot resoldre amb
UNIQUE(pels nuls), ni ambCHECK(no pot mirar altres files), i que es resol amb un índex únic parcialWHERE data_devolucio IS NULL. - 15 consultes ordenades per dificultat, cadascuna amb la seva tècnica i la seva lliçó: del filtre simple a l'anti-join, del
HAVINGa l'agregat condicional, de la correlacionada alLATERAL, i de la sèrie temporal sense buits al pivot, la finestra i la CTE recursiva. Amb cinc condicions transversals: comentari amb la definició, res de files perdudes, desempat als rànquings, resultat adjunt i ni unSELECT *. - Rendiment: índexs a les FK que es naveguen, el parcial de RI-03, un per a la sèrie temporal i un altre per a la cerca per títol, cadascun justificat amb la consulta que el fa servir, dos
EXPLAINcomentats i la llista del que decideixes no indexar. - Seguretat: tres rols de privilegi mínim, sense
DELETEsobre l'històric, informes servits per vistes agregades, baixa lògica amb anonimització prevista i dades íntegrament fictícies. - Lliurament: quatre fitxers que s'executen en ordre sobre una base buida, esquema idempotent, i la rúbrica de sis criteris —model 25 %, integritat 20 %, consultes 25 %, rendiment 10 %, seguretat 10 %, lliurament 10 %— amb quatre errors que anul·len el seu bloc sencer.
Saps què cal fer i amb què se't mesurarà. Falta el com. A la lliçó següent, Implementació del projecte, hi ha la guia de construcció en set passos: de l'enunciat al diagrama entitat-relació complet, amb les decisions difícils justificades —per què exemplars és taula, per què es desa data_prevista en lloc de calcular-la, per què les multes no són una columna i com es modela una cua—; el 01-esquema.sql comentat amb l'índex parcial explicat a fons; com generar dades coherents i quins casos límit han de contenir; el mètode per escriure consultes sense equivocar-te, amb dues resoltes d'exemple; com decidir els índexs a partir de les consultes i no al revés; què encapsular en vistes, procediments i disparadors sense passar-se; i el cronograma de treball.
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
