La lliçó anterior va construir l'instrumental: dependències funcionals, els seus tipus, els axiomes d'Armstrong, la clausura X⁺, les claus candidates i la distinció entre atributs primers i no primers. Tot això existia amb un únic propòsit, i és el d'aquesta lliçó: poder enunciar amb precisió què és una taula ben dissenyada.
Una forma normal és una condició que una relació compleix o no compleix. No és un consell ni una bona pràctica: és una propietat verificable, com dir que un nombre és parell. Cada forma normal prohibeix un tipus concret de dependència mal col·locada, i les prohibicions s'apilen: cada nivell exigeix el de l'anterior i alguna cosa més.
Aquesta lliçó és el catàleg. Recorrerem les sis formes normals que es fan servir —primera, segona, tercera, Boyce-Codd, quarta i cinquena— més una setena que només té interès teòric. De cadascuna en veurem quatre coses: la definició formal, un exemple mínim de BiblioRed que la viola amb dades concretes, la correcció, i el motiu pel qual importa. El que no farem aquí és aplicar una metodologia completa sobre una taula real: això és la lliçó 05-03. Aquí es tracta de tenir el catàleg sencer abans de començar a fer-lo servir, igual que s'aprenen les eines abans de l'obra.
Contingut
- Què significa que una relació "està en" una forma normal
- Per què les formes normals són acumulatives
- Primera forma normal (1FN): valors atòmics
- Què significa exactament "atòmic"
- Segona forma normal (2FN): sense dependències parcials
- Tercera forma normal (3FN): sense dependències transitives
- Forma normal de Boyce-Codd (FNBC): tot determinant és superclau
- Quan la descomposició a FNBC no conserva les dependències
- Quarta forma normal (4FN): dependències multivaluades
- Cinquena forma normal (5FN): dependències de reunió
- La forma normal de domini i clau (FNDC): el límit teòric
- Taula resum de totes les formes normals
- El criteri real de la indústria
- Què significa que una relació "està en" una forma normal
Comencem pel bàsic, perquè el llenguatge que es fa servir aquí confon molta gent.
Quan es diu que "la taula socis està en tercera forma normal", s'està afirmant alguna cosa sobre l'estructura de la taula i les seves dependències funcionals, no sobre les dades que conté avui. És una propietat del disseny. Si demà hi insereixes mil files més, la taula continua estant en tercera forma normal; si en canvies l'estructura o descobreixes una regla de negoci nova, pot deixar d'estar-hi.
Tres precisions importants:
Les formes normals es prediquen de relacions, no de bases de dades. No existeix "la base de dades de BiblioRed està en 3FN" com a afirmació única: cada taula s'avalua per separat. El que sí que es diu, per abreujar, és que un esquema està en 3FN quan totes les seves taules hi estan.
Calen les dependències funcionals per respondre. Mirant només el CREATE TABLE no es pot saber en quina forma normal està una taula, perquè les formes normals parlen de dependències, i les dependències vénen del negoci. Dues taules amb columnes idèntiques poden estar una en 3FN i l'altra no, si les regles que les regeixen són diferents. Això sorprèn, però és la conseqüència directa del que vam veure a la secció 6 de 05-01.
L'adjectiu "normal" no significa "habitual". Ve del vocabulari matemàtic, on "forma normal" designa una forma canònica a la qual es porta un objecte per poder-lo comparar amb altres. No té res a veure amb la normalitat estadística.
- Per què les formes normals són acumulatives
Les formes normals estan ordenades i cadascuna inclou l'anterior: per estar en 3FN cal estar abans en 2FN, i per estar en 2FN cal estar abans en 1FN. No és una convenció arbitrària: és a la definició mateixa de cada forma, que comença literalment dient "una relació està en nFN si està en (n−1)FN i, a més…".
flowchart TD
R["Relacio qualsevol<br/><i>(pot tenir llistes a les cel·les)</i>"]
N1["<b>1FN</b><br/>Valors atomics"]
N2["<b>2FN</b><br/>Sense dependencies parcials"]
N3["<b>3FN</b><br/>Sense dependencies transitives"]
BC["<b>FNBC</b><br/>Tot determinant es superclau"]
N4["<b>4FN</b><br/>Sense dependencies multivaluades"]
N5["<b>5FN</b><br/>Sense dependencies de reunio"]
DK["<b>FNDC</b><br/>Nomes dominis i claus"]
R --> N1 --> N2 --> N3 --> BC --> N4 --> N5 --> DK
style N3 fill:#2d6a4f,color:#fff
style BC fill:#2d6a4f,color:#fff
Llegeix-ho com una escala d'exigència: com més amunt, més restrictiva és la condició i menys taules la compleixen. Els dos esglaons ressaltats són l'objectiu pràctic habitual, i a la secció 13 explicarem per què.
La conseqüència lògica de l'acumulació és útil en els dos sentits:
- Si una taula no està en 2FN, tampoc no està en 3FN ni en cap de superior. N'hi ha prou amb una fallada a baix per descartar-ho tot.
- Si una taula sí que està en FNBC, automàticament està en 3FN, 2FN i 1FN. No cal comprovar-les.
Per això les comprovacions es fan de baix a dalt, i tan bon punt en falla una es corregeix abans de continuar pujant. És exactament l'ordre que seguirà el procés de la lliçó 05-03.
Un advertiment de nomenclatura abans de continuar: a la bibliografia en anglès i en algunes lliçons anteriors d'aquest curs veuràs 1NF, 2NF, 3NF i BCNF (first normal form, Boyce-Codd normal form). Són exactament el mateix que 1FN, 2FN, 3FN i FNBC. Farem servir la nomenclatura catalana.
- Primera forma normal (1FN): valors atòmics
Definició. Una relació està en primera forma normal si el valor de cada atribut a cada fila és atòmic: un sol valor indivisible del domini de l'atribut. En particular, no hi ha llistes dins d'una cel·la, ni grups de columnes repetides, ni files duplicades.
És la forma normal més bàsica i també la més malinterpretada. Anem amb un exemple de BiblioRed que ja coneixem per un altre camí.
Violació 1: la llista dins de la cel·la
La lliçó 04-01 va denunciar l'antipatró de la "llista separada per comes" i la 04-03 el va resoldre amb la regla 3 de transformació. Ara podem dir formalment què hi havia de dolent: violava la primera forma normal.
Així estava la taula de socis al full original de BiblioRed:
socis_full — no està en 1FN
| soci_id | nom | telefons |
|---|---|---|
| 14 | Marta Alsina | 600111222, 938880011 |
| 15 | Ivan Pereda | 600333444 |
| 16 | Núria Bastos | 600555666, 938880033, 617220099 |
El valor 600111222, 938880011 no és un telèfon: són dos telèfons ficats en una cadena de text. El SGBD veu una cadena i la tracta com a tal, amb conseqüències molt concretes:
No el troba, perquè el valor desat és '600111222, 938880011', que no és igual a '938880011'. La sortida habitual és el LIKE, i és pitjor que el problema:
Això funciona per casualitat i falla tan bon punt un número en contingui un altre com a subcadena. A més: no es pot posar una restricció de format sobre un telèfon individual, no es pot comptar quants telèfons hi ha sense trossejar cadenes, no es pot indexar, i no es pot impedir que el mateix número es repeteixi.
Correcció: la regla 3 del mòdul 4. Una taula nova amb la clau forana i el valor, i la clau primària formada per tots dos:
CREATE TABLE telefons_soci (
soci_id INTEGER NOT NULL,
numero VARCHAR(20) NOT NULL,
tipus VARCHAR(10) NOT NULL DEFAULT 'mobil',
CONSTRAINT pk_telefons_soci PRIMARY KEY (soci_id, numero),
CONSTRAINT fk_telefons_soci FOREIGN KEY (soci_id)
REFERENCES socis (soci_id) ON DELETE CASCADE ON UPDATE CASCADE
);telefons_soci — en 1FN
| soci_id | numero | tipus |
|---|---|---|
| 14 | 600111222 | mobil |
| 14 | 938880011 | fix |
| 15 | 600333444 | mobil |
| 16 | 600555666 | mobil |
| 16 | 938880033 | fix |
| 16 | 617220099 | feina |
Ara cada cel·la té un valor, la cerca per igualtat funciona, el CHECK de format es pot aplicar número a número, i la clau primària composta impedeix repetir un telèfon en el mateix soci. És la taula que ja és a l'esquema de BiblioRed des de la lliçó 04-03; el que és nou és saber que la seva justificació formal s'anomena primera forma normal.
Exactament el mateix cas, amb el mateix remei, és el dels idiomes de subtítols d'un DVD. Desar subtitols = 'es,ca,en' a materials_dvd és una violació de 1FN, i la solució és subtitols_dvd(material_id, idioma), amb idioma sobre un domini validat. Un altre cop: la taula ja existia; ara en sabem el nom formal.
Violació 2: el grup repetitiu
La segona manera de trencar la 1FN és més subtil perquè cada cel·la sí que té un valor únic. El problema és l'estructura de columnes:
socis_full_v2 — tampoc no està en 1FN
| soci_id | nom | telefon_1 | telefon_2 | telefon_3 |
|---|---|---|---|---|
| 14 | Marta Alsina | 600111222 | 938880011 | (NULL) |
| 15 | Ivan Pereda | 600333444 | (NULL) | (NULL) |
| 16 | Núria Bastos | 600555666 | 938880033 | 617220099 |
És l'antipatró de les "columnes numerades" de 04-01. Cada cel·la és atòmica, sí, però el grup telefon_1, telefon_2, telefon_3 és un atribut multivaluat disfressat de tres columnes, i les tres columnes no són tres atributs diferents: són tres ocurrències del mateix. Els símptomes ho delaten:
-- Buscar el soci amb el telèfon 938880011: cal mirar a les tres.
SELECT * FROM socis_full_v2
WHERE telefon_1 = '938880011'
OR telefon_2 = '938880011'
OR telefon_3 = '938880011';I si demà un soci té quatre telèfons, cal fer un ALTER TABLE —canviar l'esquema per un canvi de dades— i reescriure totes les consultes. La correcció és la mateixa taula telefons_soci d'abans.
Un matís: les files duplicades
La definició clàssica de relació (la de 02-01) diu que una relació és un conjunt de tuples, i en un conjunt no hi ha elements repetits. Per tant, en teoria estricta, dues files idèntiques violen la 1FN.
A la pràctica, SQL permet taules sense clau primària i amb files duplicades. La recomanació operativa és inequívoca i ja la seguim des del mòdul 2: tota taula ha de tenir clau primària declarada. Amb això, les files duplicades són impossibles i aquest aspecte de la 1FN queda garantit pel SGBD.
- Què significa exactament "atòmic"
Aquí cal ser honest, perquè és on la 1FN genera més discussions estèrils.
"Atòmic" no és una propietat de la dada: és una propietat de la relació entre la dada i l'ús que se'n fa. Un valor és atòmic si l'aplicació mai no necessita mirar-hi a dins per fer la seva feina.
Mira aquests quatre casos de BiblioRed:
| Valor | Atòmic? | Per què |
|---|---|---|
'Carrer Major, 12, 08110 Vallmar' en una columna adreca |
Depèn | Si només s'imprimeix en una etiqueta, sí. Si cal agrupar préstecs per codi postal, no: el CP s'ha d'extreure amb funcions de text, i aleshores l'adreça havia d'estar descomposta |
'2026-04-09' en una columna DATE |
Sí | Encara que conté any, mes i dia, el tipus DATE els exposa com a funcions (EXTRACT(YEAR FROM …)) sense trossejar text. El SGBD entén l'estructura interna |
'600111222, 938880011' a telefons |
No | Cal partir la cadena per fer servir qualsevol dels dos, i no hi ha cap tipus que doni sentit a la coma |
'{"p1": 4, "p2": 5}' en una columna JSONB d'informes_esdeveniment |
Sí, a la pràctica | El SGBD té operadors natius (->, @>), índexs GIN i validació d'estructura. No estàs partint text: estàs consultant un tipus compost que PostgreSQL entén |
El cas del JSONB mereix un paràgraf, perquè és on la 1FN de 1970 es troba amb les bases de dades d'avui. Els puristes dirien que una columna JSONB amb diversos valors a dins viola la 1FN. A la pràctica s'accepta quan es compleixen dues condicions: el contingut no participa en cap relació amb altres taules (no hi ha claus foranes cap a dins del JSON) i no hi ha regles de negoci que depenguin dels seus camps individuals. A BiblioRed, informes_esdeveniment.respostes_enquesta compleix totes dues: són respostes lliures a un qüestionari que només es llegeixen senceres per generar un informe. Si demà calgués agregar per pregunta, calcular mitjanes per ítem o posar restriccions, deixaria d'estar justificat i caldria treure-ho a una taula respostes_enquesta(esdeveniment_id, soci_id, pregunta, valor).
Aquesta discussió ja la vam tenir amb un altre vocabulari a 03-04, en parlar de jsonb a PostgreSQL. La regla resumida:
Desa un valor compost només si el faràs servir sempre sencer. Tan bon punt necessitis buscar, filtrar, agrupar o restringir per una de les seves parts, aquesta part ha de ser una columna o una fila.
- Segona forma normal (2FN): sense dependències parcials
Definició. Una relació està en segona forma normal si està en 1FN i, a més, tot atribut no primer depèn funcionalment de la clau candidata completa, i no d'una part d'ella. Dit d'una altra manera: no existeixen dependències parcials d'atributs no primers respecte de cap clau candidata.
Recordem el vocabulari de 05-01: un atribut no primer és el que no forma part de cap clau candidata, i una dependència és parcial quan un subconjunt propi de la clau ja determina l'atribut.
De la definició se'n segueix una drecera enorme:
Si totes les claus candidates són d'un sol atribut, la relació està automàticament en 2FN. No hi ha "parts" d'una clau d'un sol atribut, així que no hi pot haver dependències parcials.
Per això la 2FN només és un problema en taules amb clau composta: taules d'unió, entitats febles, i taules planes heretades de fulls de càlcul.
Exemple mínim: el detall de préstecs
BiblioRed va avaluar en el seu moment permetre que un soci s'endugués diversos exemplars en una sola operació de taulell, amb un "préstec" que agrupa diverses línies. Algú va proposar aquesta taula:
prestec_linies — en 1FN però NO en 2FN
Clau candidata: {prestec_id, exemplar_id} (un exemplar apareix una sola vegada en un préstec).
| prestec_id | exemplar_id | data_prestec | soci_id | exemplar_estat | exemplar_sucursal |
|---|---|---|---|---|---|
| 5001 | 3081 | 2026-04-09 | 14 | prestat | Nord |
| 5001 | 3082 | 2026-04-09 | 14 | prestat | Nord |
| 5001 | 3090 | 2026-04-09 | 14 | prestat | Nord |
| 5002 | 3095 | 2026-04-10 | 16 | prestat | Sud |
| 5003 | 3081 | 2026-05-02 | 15 | prestat | Nord |
Les dependències, segons les regles de negoci:
d1: {prestec_id, exemplar_id} → (res exclusiu seu)
d2: prestec_id → {data_prestec, soci_id} ← PARCIAL
d3: exemplar_id → {exemplar_estat, exemplar_sucursal} ← PARCIALHi ha dues dependències parcials, una per cada meitat de la clau. I les seves conseqüències són visibles a la taula de dalt: 2026-04-09 i 14 estan escrits tres vegades perquè el préstec 5001 té tres línies; Nord està escrit tres vegades per a l'exemplar 3081 perquè aquest exemplar apareix en dos préstecs.
Les anomalies corresponents són les de sempre:
-- Anomalia d'actualització: l'exemplar 3081 es trasllada a la sucursal Centre.
-- Cal tocar TOTES les línies on apareix, en tots els préstecs històrics.
UPDATE prestec_linies SET exemplar_sucursal = 'Centre' WHERE exemplar_id = 3081;-- Anomalia d'inserció: no es pot registrar un exemplar nou
-- que encara no s'ha prestat mai.
INSERT INTO prestec_linies (exemplar_id, exemplar_estat, exemplar_sucursal)
VALUES (3096, 'disponible', 'Est');Correcció: cada dependència parcial es treu a la seva pròpia taula, amb la part de la clau que la determina com a clau primària.
-- El que depèn de prestec_id tot sol
CREATE TABLE prestecs (
prestec_id INTEGER GENERATED BY DEFAULT AS IDENTITY,
data_prestec DATE NOT NULL,
soci_id INTEGER NOT NULL,
CONSTRAINT pk_prestecs PRIMARY KEY (prestec_id),
CONSTRAINT fk_prestecs_soci FOREIGN KEY (soci_id) REFERENCES socis (soci_id)
);
-- El que depèn d'exemplar_id tot sol
CREATE TABLE exemplars (
exemplar_id INTEGER GENERATED BY DEFAULT AS IDENTITY,
estat VARCHAR(20) NOT NULL,
sucursal_id INTEGER NOT NULL,
CONSTRAINT pk_exemplars PRIMARY KEY (exemplar_id),
CONSTRAINT fk_exemplars_sucursal FOREIGN KEY (sucursal_id)
REFERENCES sucursals (sucursal_id)
);
-- El que depèn de la clau completa: només l'associació
CREATE TABLE prestec_linies (
prestec_id INTEGER NOT NULL,
exemplar_id INTEGER NOT NULL,
CONSTRAINT pk_prestec_linies PRIMARY KEY (prestec_id, exemplar_id),
CONSTRAINT fk_pl_prestec FOREIGN KEY (prestec_id) REFERENCES prestecs (prestec_id),
CONSTRAINT fk_pl_exemplar FOREIGN KEY (exemplar_id) REFERENCES exemplars (exemplar_id)
);Resultat, amb les mateixes dades:
prestecs
| prestec_id | data_prestec | soci_id |
|---|---|---|
| 5001 | 2026-04-09 | 14 |
| 5002 | 2026-04-10 | 16 |
| 5003 | 2026-05-02 | 15 |
exemplars
| exemplar_id | estat | sucursal_id |
|---|---|---|
| 3081 | prestat | 2 |
| 3082 | prestat | 2 |
| 3090 | prestat | 2 |
| 3095 | prestat | 3 |
prestec_linies
| prestec_id | exemplar_id |
|---|---|
| 5001 | 3081 |
| 5001 | 3082 |
| 5001 | 3090 |
| 5002 | 3095 |
| 5003 | 3081 |
Compta les repeticions: la data 2026-04-09 apareix una vegada en comptes de tres. La sucursal de l'exemplar 3081 apareix una vegada en comptes de dues. I ara sí que es pot donar d'alta un exemplar que encara no ha demanat ningú. Fixa't a més que prestec_linies s'ha quedat només amb la clau: és una taula d'associació pura, i això és perfectament correcte —significa que l'únic fet que aporta és "aquest exemplar va formar part d'aquest préstec".
- Tercera forma normal (3FN): sense dependències transitives
Definició. Una relació està en tercera forma normal si està en 2FN i, a més, cap atribut no primer no depèn transitivament de cap clau candidata. Equivalentment: per a tota dependència no trivial
X → Aque es compleixi a la relació, o béXés superclau, o béAés un atribut primer.
La segona formulació és la que es fa servir per comprovar, perquè és mecànica: recorres les dependències una a una i a cadascuna li fas dues preguntes.
La idea intuïtiva és la de sempre: un atribut no primer no ha de dependre d'un altre atribut no primer. Si ho fa, és que aquest segon atribut és en realitat la clau d'una altra entitat que s'ha colat a la taula.
Exemple mínim: el codi postal de la sucursal
Aquesta és la violació de 3FN de manual, i BiblioRed la té servida.
sucursals_v0 — en 2FN però NO en 3FN
Clau candidata: {sucursal_id} (un sol atribut, així que la 2FN està garantida).
| sucursal_id | nom | carrer | cp | ciutat |
|---|---|---|---|---|
| 1 | Centre | Plaça de la Vila, 3 | 08100 | Vallmar |
| 2 | Nord | Carrer Major, 12 | 08110 | Vallmar |
| 3 | Sud | Avinguda del Port, 45 | 08130 | Vallmar de Mar |
| 4 | Est | Carrer del Bosc, 8 | 08110 | Vallmar |
Dependències:
La segona surt d'una regla del negoci real: un codi postal pertany a un sol municipi. I produeix una dependència transitiva sucursal_id → cp → ciutat, amb cp que no és superclau (dues sucursals comparteixen el 08110) i ciutat que no és primer.
Aplicant la formulació mecànica a cp → ciutat:
- És
cpsuperclau?{cp}⁺ = {cp, ciutat}. No conté tots els atributs. No. - És
ciutatprimer? L'única clau candidata és{sucursal_id}, iciutatno hi és. No.
Les dues respostes són "no", per tant viola la 3FN.
I l'anomalia és immediata:
-- El municipi de Vallmar de Mar es fusiona i passa a dir-se Vallmar Marina.
-- Totes les sucursals del 08130 han de canviar. Si alguna s'escapa:
UPDATE sucursals_v0 SET ciutat = 'Vallmar Marina' WHERE sucursal_id = 3;
-- Ara imagina que hi hagués una sucursal 5 també al 08130 i no s'actualitzés.
-- La base de dades afirmaria que el 08130 és a dos municipis diferents,
-- cosa que és una contradicció amb la regla de negoci e2.
SELECT cp, COUNT(DISTINCT ciutat) FROM sucursals_v0 GROUP BY cp HAVING COUNT(DISTINCT ciutat) > 1;Correcció: treure la dependència transitiva a la seva pròpia taula, amb el determinant com a clau primària, i deixar a l'original una clau forana.
CREATE TABLE codis_postals (
cp VARCHAR(5) NOT NULL,
ciutat VARCHAR(60) NOT NULL,
CONSTRAINT pk_codis_postals PRIMARY KEY (cp)
);
CREATE TABLE sucursals (
sucursal_id INTEGER GENERATED BY DEFAULT AS IDENTITY,
nom VARCHAR(60) NOT NULL,
carrer VARCHAR(120) NOT NULL,
cp VARCHAR(5) NOT NULL,
CONSTRAINT pk_sucursals PRIMARY KEY (sucursal_id),
CONSTRAINT uq_sucursals_nom UNIQUE (nom),
CONSTRAINT fk_sucursals_cp FOREIGN KEY (cp)
REFERENCES codis_postals (cp) ON UPDATE CASCADE
);codis_postals
| cp | ciutat |
|---|---|
| 08100 | Vallmar |
| 08110 | Vallmar |
| 08130 | Vallmar de Mar |
sucursals
| sucursal_id | nom | carrer | cp |
|---|---|---|---|
| 1 | Centre | Plaça de la Vila, 3 | 08100 |
| 2 | Nord | Carrer Major, 12 | 08110 |
| 3 | Sud | Avinguda del Port, 45 | 08130 |
| 4 | Est | Carrer del Bosc, 8 | 08110 |
Ara el municipi de cada codi postal està escrit una vegada. Canviar-lo és un UPDATE d'una fila, i no hi ha cap manera física que el 08110 aparegui en dues ciutats. És la mateixa taula sucursals que fem servir des del mòdul 2, ara amb la seva justificació formal.
(Nota pràctica: en un sistema petit amb quatre sucursals, treure una taula de codis postals pot semblar excessiu, i de fet l'esquema real de BiblioRed desa dir_ciutat directament a sucursals. És una decisió defensable —una desnormalització conscient sobre una taula de quatre files que gairebé mai no canvia—, però convé prendre-la sabent que és una desnormalització, no per no haver vist la dependència. Sobre com prendre aquesta decisió amb criteri va tota la lliçó 05-04.)
La diferència entre 2FN i 3FN, en una frase
Totes dues prohibeixen que un atribut no primer depengui d'alguna cosa que no sigui la clau completa. Canvia de què depèn indegudament:
| Depèn indegudament de… | Només pot passar si… | |
|---|---|---|
| 2FN | Una part de la clau | La clau és composta |
| 3FN | Un altre atribut no primer | Hi ha atributs no primers que en determinen d'altres |
- Forma normal de Boyce-Codd (FNBC): tot determinant és superclau
La tercera forma normal deixa una escletxa oberta. La seva definició perdona una dependència X → A si A resulta ser un atribut primer, encara que X no sigui superclau. Raymond Boyce i Edgar Codd van proposar el 1974 tancar aquesta escletxa, i el resultat és una definició molt més simple:
Definició. Una relació està en forma normal de Boyce-Codd si, per a tota dependència funcional no trivial
X → Yque s'hi compleixi,Xés superclau.
Sense excepcions, sense distingir primers de no primers. És la definició més neta de totes les formes normals, i per això molts autors l'ensenyen abans que la 3FN.
Comparades costat per costat:
| Forma | Per a tota dependència no trivial X → A… |
|---|---|
| 3FN | X és superclau o A és primer |
| FNBC | X és superclau |
FNBC és estrictament més exigent. Tota relació en FNBC està en 3FN; el contrari no sempre.
El cas clàssic: en 3FN però no en FNBC
Perquè aparegui la diferència calen tres condicions simultànies, i per això el cas és infreqüent: la relació ha de tenir diverses claus candidates, aquestes claus han de ser compostes, i s'han de encavalcar (compartir algun atribut).
Construïm-lo a BiblioRed. Suposem —i això és una hipòtesi per a l'exemple, no la regla real de l'esquema del mòdul 4— que BiblioRed imposa dues normes en assignar ponents als esdeveniments:
- N1: en un esdeveniment, cada rol el desenvolupa una sola persona. No hi ha dos moderadors a la mateixa xerrada.
- N2: cada ponent del registre té un rol fix assignat per contracte. Clara Ferran és sempre moderadora; mai no fa de tallerista.
assignacions — en 3FN però NO en FNBC
| esdeveniment_id | ponent_id | rol |
|---|---|---|
| 210 | 7 | moderador |
| 210 | 9 | tallerista |
| 211 | 7 | moderador |
| 211 | 12 | autor_convidat |
| 212 | 9 | tallerista |
Les dependències que surten de N1 i N2:
Claus candidates. Calculem clausures:
{esdeveniment_id, rol}⁺={esdeveniment_id, rol, ponent_id}= tots. Superclau. Mínima (niesdeveniment_idnirolno basten tots sols). Clau candidata 1.{esdeveniment_id, ponent_id}⁺: perh2hi entrarol, i ja els tenim tots. Superclau. Mínima. Clau candidata 2.
Dues claus candidates, compostes, i encavalcades: totes dues contenen esdeveniment_id. Es compleixen les tres condicions.
Atributs primers: esdeveniment_id (a totes dues), rol (a la 1), ponent_id (a la 2). Els tres atributs són primers, i no n'hi ha cap de no primer.
Està en 3FN? La 3FN exigeix que en tota dependència no trivial X → A, o bé X sigui superclau o bé A sigui primer. Repassem:
h1:X = {esdeveniment_id, rol}és superclau. Compleix.h2:X = {ponent_id}no és superclau ({ponent_id}⁺ = {ponent_id, rol}, hi falten atributs). PeròA = rolsí que és primer. Compleix per la segona via.
Sí que està en 3FN.
Està en FNBC? La FNBC només admet la primera via:
h2:ponent_idno és superclau. Incompleix.
No està en FNBC.
I quin mal fa a la pràctica? Mira-ho a les dades: el fet "el ponent 7 és moderador" està escrit dues vegades (files 210 i 211), i el fet "el ponent 9 és tallerista", dues més. Si Clara Ferran renegocia el seu contracte i passa a tallerista, cal actualitzar totes les seves files o la base de dades dirà que és moderadora en uns esdeveniments i tallerista en uns altres, contradient N2. És una anomalia d'actualització clàssica, dins d'una taula que està en 3FN. Això és exactament el que la FNBC ve a caçar.
A més, l'anomalia d'inserció: no es pot registrar que un ponent nou té el rol de tallerista fins que se l'assigni a algun esdeveniment.
Correcció: la regla és sempre la mateixa. Es treu la dependència infractora a la seva pròpia taula, amb el determinant com a clau primària.
CREATE TABLE ponent_rol (
ponent_id INTEGER NOT NULL,
rol VARCHAR(25) NOT NULL,
CONSTRAINT pk_ponent_rol PRIMARY KEY (ponent_id),
CONSTRAINT fk_ponent_rol FOREIGN KEY (ponent_id) REFERENCES ponents (ponent_id)
);
CREATE TABLE assignacions (
esdeveniment_id INTEGER NOT NULL,
ponent_id INTEGER NOT NULL,
CONSTRAINT pk_assignacions PRIMARY KEY (esdeveniment_id, ponent_id),
CONSTRAINT fk_assig_esdeveniment FOREIGN KEY (esdeveniment_id) REFERENCES esdeveniments (esdeveniment_id),
CONSTRAINT fk_assig_ponent FOREIGN KEY (ponent_id) REFERENCES ponents (ponent_id)
);ponent_rol
| ponent_id | rol |
|---|---|
| 7 | moderador |
| 9 | tallerista |
| 12 | autor_convidat |
assignacions
| esdeveniment_id | ponent_id |
|---|---|
| 210 | 7 |
| 210 | 9 |
| 211 | 7 |
| 211 | 12 |
| 212 | 9 |
Cada rol de ponent, escrit una vegada. Les dues taules estan en FNBC.
- Quan la descomposició a FNBC no conserva les dependències
I ara la lletra petita, que és el que fa que la FNBC no sigui sempre la millor idea.
Torna a mirar la descomposició que acabem de fer i fes-te aquesta pregunta: on ha quedat la regla N1 ("en un esdeveniment, cada rol el desenvolupa una sola persona")?
Formalment, la dependència h1: {esdeveniment_id, rol} → ponent_id. A la descomposició:
- A
ponent_rolno hi és: no hi haesdeveniment_id. - A
assignacionsno hi és: no hi harol.
La dependència ha desaparegut de totes dues taules. I la seva desaparició té una conseqüència pràctica molt concreta: ja no hi ha cap combinació de PRIMARY KEY, UNIQUE o CHECK que impedeixi això:
INSERT INTO assignacions (esdeveniment_id, ponent_id) VALUES (213, 7); -- moderador
INSERT INTO assignacions (esdeveniment_id, ponent_id) VALUES (213, 20); -- un altre moderadorSi el ponent 20 també és moderador segons ponent_rol, acabem de ficar dos moderadors al mateix esdeveniment, violant N1, i el SGBD no ha protestat perquè les claus primàries de totes dues taules es respecten.
Per comprovar N1 cal fer un JOIN de les dues taules:
-- Detectar violacions de N1: esdeveniments amb dues persones al mateix rol
SELECT a.esdeveniment_id, r.rol, COUNT(*) AS persones
FROM assignacions a
JOIN ponent_rol r ON r.ponent_id = a.ponent_id
GROUP BY a.esdeveniment_id, r.rol
HAVING COUNT(*) > 1;I una consulta no és una restricció: cal executar-la, i per aleshores la dada dolenta ja és a dins.
Definició. Una descomposició conserva les dependències si cada dependència funcional del conjunt original es pot comprovar mirant una sola taula de la descomposició, sense necessitat de reunir-les.
El resultat teòric és aquest, i convé conèixer-lo:
- Sempre existeix una descomposició en 3FN que és alhora sense pèrdua d'informació i conservadora de les dependències.
- No sempre existeix una descomposició en FNBC que conservi les dependències.
El nostre exemple és precisament un cas dels segons. Davant d'ell cal triar:
| Opció | Què es guanya | Què es perd |
|---|---|---|
Quedar-se en 3FN (taula assignacions original) |
N1 la garanteix la clau primària {esdeveniment_id, rol} |
El rol de cada ponent es repeteix; anomalies d'actualització |
| Descompondre a FNBC | Cada rol escrit una vegada; sense anomalies | N1 deixa de ser verificable dins d'una taula; cal garantir-la amb un activador o a l'aplicació |
No hi ha una resposta universal. La decisió depèn de quina dependència s'incompleix més sovint a la pràctica i de quina és més greu. El que no és acceptable és descompondre a FNBC i no adonar-se que s'ha perdut una regla de negoci: aleshores la regla simplement deixa de complir-se i ningú no se n'assabenta fins que algú pregunta per què hi ha dos moderadors.
Sobre com es verifiquen formalment aquestes dues propietats —conservació de dependències i descomposició sense pèrdua— i sobre la condició de Heath que garanteix la segona, va bona part de la lliçó 05-03.
(Un apunt per evitar confusions: la taula participacions de l'esquema real de BiblioRed, amb clau {esdeveniment_id, ponent_id, rol}, no té aquest problema, perquè allà no regeix N2: un ponent pot fer de moderador en un esdeveniment i de tallerista en un altre. Sense la dependència ponent_id → rol, participacions està en FNBC. L'exemple d'aquesta secció és una variant hipotètica construïda per il·lustrar el cas.)
- Quarta forma normal (4FN): dependències multivaluades
Fins aquí, tot ha girat al voltant de dependències funcionals. Existeix un altre tipus de dependència que les funcionals no capturen, i per a la qual cal una forma normal pròpia.
El problema, primer
BiblioRed organitza l'esdeveniment 210, "Club de lectura: novel·la històrica". Aquest esdeveniment té:
- Dos ponents: el 7 i el 9.
- Tres materials recomanats: 101, 102 i 103.
I aquí hi ha la dada clau: els ponents i els materials no tenen res a veure entre si. El ponent 7 no està associat a un material concret; els tres materials són de l'esdeveniment, no d'un ponent. Són dues llistes independents que pengen del mateix esdeveniment.
Ara imagina que algú les fica en una sola taula:
esdeveniment_recursos — en FNBC però NO en 4FN
| esdeveniment_id | ponent_id | material_id |
|---|---|---|
| 210 | 7 | 101 |
| 210 | 7 | 102 |
| 210 | 7 | 103 |
| 210 | 9 | 101 |
| 210 | 9 | 102 |
| 210 | 9 | 103 |
Sis files per representar dos fets i tres fets. 2 × 3 = 6: la taula és un producte cartesià disfressat. Amb tres ponents i vuit materials serien 24 files per a onze fets.
Observa què té d'estrany l'assumpte: no hi ha cap dependència funcional problemàtica. L'única clau candidata és la taula sencera, {esdeveniment_id, ponent_id, material_id}; tots els atributs són primers; no hi ha cap determinant que no sigui superclau. La taula està en FNBC i tot i així és un desastre:
-- Anomalia d'inserció: afegir un quart material a l'esdeveniment 210
-- obliga a inserir UNA FILA PER PONENT, o les dades queden inconsistents.
INSERT INTO esdeveniment_recursos VALUES (210, 7, 104);
-- Si oblides aquesta segona, la taula diu que el material 104
-- va amb el ponent 7 però no amb el 9, cosa que no significa res.
INSERT INTO esdeveniment_recursos VALUES (210, 9, 104);-- Anomalia d'esborrament: donar de baixa el ponent 9 obliga a esborrar 3 files.
DELETE FROM esdeveniment_recursos WHERE esdeveniment_id = 210 AND ponent_id = 9;La dependència multivaluada
Definició. En una relació
R, hi ha una dependència multivaluada deYrespecte deX, escritaX ↠ Y(amb doble punta de fletxa), si el conjunt de valors deYassociats a un valor deXdepèn només deXi és independent dels altres atributs de la relació.
Es llegeix "X multidetermina Y". En el nostre cas:
"L'esdeveniment multidetermina els seus ponents": la llista de ponents d'un esdeveniment és la que és, sense que importi quins materials tingui. I a la inversa.
Una dependència multivaluada sempre ve en parella: si X ↠ Y en una relació amb atributs X, Y, Z, aleshores també X ↠ Z. Per això s'escriuen juntes: esdeveniment_id ↠ ponent_id | material_id.
Nota la relació entre els dos tipus de dependència: tota dependència funcional és una dependència multivaluada (si X → Y, el conjunt de valors de Y per a cada X té exactament un element i no depèn de res més). El contrari no: esdeveniment_id ↠ ponent_id no és funcional, perquè un esdeveniment té diversos ponents.
Definició. Una relació està en quarta forma normal si està en FNBC i, per a tota dependència multivaluada no trivial
X ↠ Y,Xés superclau.
A esdeveniment_recursos, esdeveniment_id ↠ ponent_id és no trivial i esdeveniment_id no és superclau (no determina la fila sencera). Viola la 4FN.
Correcció: separar les dues llistes independents en dues taules. Que és, exactament, el que l'esquema del mòdul 4 ja fa:
CREATE TABLE participacions (
esdeveniment_id INTEGER NOT NULL,
ponent_id INTEGER NOT NULL,
rol VARCHAR(25) NOT NULL,
CONSTRAINT pk_participacions PRIMARY KEY (esdeveniment_id, ponent_id, rol),
CONSTRAINT fk_part_esdeveniment FOREIGN KEY (esdeveniment_id) REFERENCES esdeveniments (esdeveniment_id),
CONSTRAINT fk_part_ponent FOREIGN KEY (ponent_id) REFERENCES ponents (ponent_id)
);
CREATE TABLE esdeveniments_materials (
esdeveniment_id INTEGER NOT NULL,
material_id INTEGER NOT NULL,
paper VARCHAR(15) NOT NULL DEFAULT 'recomanat',
CONSTRAINT pk_esdeveniments_materials PRIMARY KEY (esdeveniment_id, material_id),
CONSTRAINT fk_em_esdeveniment FOREIGN KEY (esdeveniment_id) REFERENCES esdeveniments (esdeveniment_id),
CONSTRAINT fk_em_material FOREIGN KEY (material_id) REFERENCES materials (material_id)
);participacions (2 files) + esdeveniments_materials (3 files) = 5 files en comptes de 6. I, més important que l'estalvi: afegir un material és una fila, i donar de baixa un ponent és una fila.
Aquest és un bon moment per assenyalar una cosa encoratjadora. Quan al mòdul 4 vam decidir, per comprensió del domini, que els ponents i els materials d'un esdeveniment eren dues relacions N:M diferents i els vam donar dues taules, estàvem posant l'esquema en quarta forma normal sense haver-ne sentit a parlar. El disseny per comprensió del domini i la normalització formal arriben gairebé sempre al mateix lloc; la normalització serveix per verificar-ho i per als casos en què la intuïció falla.
L'avís important sobre la 4FN
La 4FN només es viola quan hi ha dues o més relacions multivaluades independents a la mateixa taula. Si les dues llistes no fossin independents —per exemple, si cada material estigués associat a un ponent concret ("el material 101 el porta el ponent 7")— aleshores la taula de tres columnes seria correcta i necessària: representaria una relació ternària genuïna, de les que vam veure a 04-02 i vam resoldre amb la regla 9 a 04-03.
La pregunta de diagnòstic és sempre la mateixa: el conjunt de valors d'aquesta columna per a un esdeveniment canvia segons el valor de l'altra columna? Si la resposta és no, són independents i cal separar-les.
- Cinquena forma normal (5FN): dependències de reunió
La cinquena forma normal, també anomenada forma normal de projecció-reunió, és l'últim esglaó amb contingut pràctic, i cal dir amb honestedat que rarament apareix a la vida real. S'explica aquí perquè sàpigues que existeix i perquè reconeguis el cas si algun dia te'l trobes, no perquè l'hagis d'aplicar el mes que ve.
La idea generalitza l'anterior. La 4FN parla de taules que es poden partir en dues sense perdre informació. La 5FN parla de taules que no es poden partir en dues, però sí en tres o més.
Definició. Una relació està en cinquena forma normal si tota dependència de reunió que s'hi compleix està implicada per les seves claus candidates. Una dependència de reunió existeix quan la relació és igual a la reunió (JOIN) de diverses de les seves projeccions.
L'exemple típic requereix una regla de negoci cíclica. Suposem que BiblioRed adquireix fons amb aquesta norma:
N3: si una sucursal treballa amb una editorial, i aquesta editorial publica una col·lecció, i aquesta col·lecció és present en aquella sucursal, aleshores aquella sucursal compra aquella col·lecció d'aquella editorial.
Sona rebuscat, i ho és: per això la 5FN és rara. Però si aquesta regla es compleix, la taula compres(sucursal, editorial, colleccio) és reconstruïble exactament a partir de tres projeccions —(sucursal, editorial), (editorial, colleccio) i (sucursal, colleccio)— i desar-la sencera és redundant.
El que cal retenir:
- Una violació de 5FN només apareix quan existeix una regla cíclica d'aquest tipus entre tres o més atributs.
- Detectar-la exigeix una anàlisi del domini molt fina i errar en el diagnòstic produeix descomposicions que inventen files en reunir (el problema de la pèrdua d'informació, que veurem a 05-03).
- Tota relació en 4FN la clau candidata de la qual sigui la relació sencera o les dependències de la qual siguin totes funcionals sol estar ja en 5FN.
Recomanació pràctica: no busquis violacions de 5FN de manera sistemàtica. Si una taula de tres o més claus foranes et resulta sospitosament redundant i detectes una regla de negoci cíclica, aleshores investiga-la. En cas contrari, no hi és.
- La forma normal de domini i clau (FNDC): el límit teòric
Ronald Fagin va definir el 1981 la forma normal de domini i clau:
Una relació està en FNDC si totes les restriccions que ha de complir són conseqüència lògica únicament de les restriccions de domini (els valors permesos a cada columna) i de les restriccions de clau (les claus candidates).
És una definició elegantíssima i amb una propietat notable: una relació en FNDC no té cap anomalia de modificació de cap tipus, ni les conegudes ni les que es poguessin descobrir en el futur. És el sostre teòric de la normalització.
El seu problema és doble: no existeix cap algorisme general per portar una relació a FNDC, i moltes relacions no admeten cap descomposició que les hi porti. Per exemple, la regla de BiblioRed "la suma dels pagaments d'una multa no pot superar el seu import" no és reduïble a dominis ni a claus, i per tant cap taula que la necessiti no estarà mai en FNDC. La solució pràctica per a aquestes regles és la que ja coneixem de 04-04: un activador o la lògica de l'aplicació.
Menciona-la si algú t'ho pregunta; no la persegueixis.
- Taula resum de totes les formes normals
| Forma | Què prohibeix | Com es detecta | Com es corregeix |
|---|---|---|---|
| 1FN | Valors no atòmics: llistes en una cel·la, grups de columnes repetides, files duplicades | Buscar comes o separadors dins de valors; columnes amb sufix numèric (tel_1, tel_2); taules sense PK |
Treure l'atribut multivaluat a una taula pròpia amb la clau forana + el valor com a PK composta |
| 2FN | Dependències parcials: un atribut no primer depèn de part de la clau composta | Només possible amb clau composta. Per a cada part de la clau, calcular-ne la clausura: si conté atributs no primers, hi ha dependència parcial | Treure cada dependència parcial a una taula nova amb aquella part de la clau com a PK |
| 3FN | Dependències transitives: un atribut no primer depèn d'un altre no primer | Per a cada dependència no trivial X → A: X és superclau? A és primer? Si les dues respostes són "no", es viola |
Treure la dependència a una taula nova amb X com a PK; deixar X com a clau forana |
| FNBC | Qualsevol determinant que no sigui superclau | Per a cada dependència no trivial X → A: X és superclau? Si no, es viola |
Igual que 3FN. Atenció: pot no conservar les dependències |
| 4FN | Dependències multivaluades independents a la mateixa taula | Taula de 3+ atributs on el nombre de files és el producte de dues llistes independents | Separar en una taula per cada llista independent |
| 5FN | Dependències de reunió no implicades per les claus | Regla de negoci cíclica entre 3+ atributs; la taula és reconstruïble reunint 3+ projeccions | Descompondre en les projeccions corresponents |
| FNDC | Tota restricció que no sigui de domini o de clau | No hi ha algorisme general | No hi ha mètode general; sovint inabastable |
I una taula complementària que ajuda a decidir per on començar a buscar:
| Si la taula… | Aleshores… |
|---|---|
| Té clau d'un sol atribut | Està automàticament en 2FN. Comença a comprovar per la 3FN |
| Té només dos atributs | Està automàticament en FNBC |
| No té atributs no primers (tots són part d'alguna clau) | Està automàticament en 3FN; comprova FNBC i 4FN |
| Té una sola clau candidata i està en 3FN | Està automàticament en FNBC |
L'última fila és especialment útil: si la taula té una única clau candidata, 3FN i FNBC coincideixen. Com que la immensa majoria de les taules amb clau subrogada tenen una sola clau candidata, a la pràctica arribar a 3FN sol significar haver arribat a FNBC.
- El criteri real de la indústria
Amb set formes normals sobre la taula, la pregunta obligada és: fins on cal arribar?
La resposta que s'aplica pràcticament a tots els sistemes transaccionals seriosos és aquesta:
Fins a 3FN o FNBC, sempre. Més enllà, només si el cas concret ho demana.
I les raons són sòlides, no una excusa per treballar menys:
Fins a 3FN/FNBC el benefici és enorme i el cost és baix. Eliminar dependències parcials i transitives treu la pràctica totalitat de les anomalies que es produeixen en un sistema real, i les taules resultants són les que qualsevol professional del sector espera trobar. A més, les descomposicions són fàcils de raonar i d'explicar.
Més enllà de FNBC, el rendiment decreixent és brusc. Les violacions de 4FN són poc freqüents i, quan apareixen, solen ser tan evidents (una taula amb sis files on n'haurien de cabre cinc de fets) que es detecten pel sentit comú. Les de 5FN són raríssimes, difícils de diagnosticar i fàcils de diagnosticar malament.
Un esquema en 3FN és un esquema que els altres entenen. Això té més pes del que sembla. Si d'aquí a tres anys algú ha de mantenir la base de dades de BiblioRed, hi trobarà taules amb la forma que espera. Un esquema descompost fins a la cinquena forma normal, amb taules que existeixen per raons que només s'entenen amb el paper de les dependències al davant, és un esquema que la següent persona trencarà sense voler.
El criteri operatiu, doncs:
| Nivell | Quan | Esforç |
|---|---|---|
| 1FN | Sempre, sense excepció. Una violació de 1FN trenca les consultes | Obligatori |
| 2FN | Sempre. Revisa tota taula amb clau composta | Obligatori |
| 3FN | Sempre. És l'objectiu per defecte | Obligatori |
| FNBC | Quan apareguin claus candidates encavalcades. Valorar si val la pena perdre la conservació de dependències | Recomanat, amb criteri |
| 4FN | Quan detectis una taula que multiplica files de dues llistes independents | Només si apareix |
| 5FN | Quan existeixi una regla de negoci cíclica documentada | Gairebé mai |
| FNDC | Mai com a objectiu de disseny | Interès teòric |
I el corol·lari, que anticipa l'última lliçó del mòdul: normalitza fins a 3FN/FNBC com a línia de base, i només aleshores, amb dades de rendiment a la mà, considera retrocedir deliberadament. Retrocedir des d'un disseny normalitzat és una decisió informada; no haver normalitzat mai és simplement no haver fet la feina.
Errors Habituals i Consells
Creure que es pot determinar la forma normal mirant només la taula. No es pot. Sense conèixer les dependències funcionals —és a dir, sense conèixer les regles del negoci— qualsevol resposta és una suposició. Si algú t'ensenya un CREATE TABLE i et pregunta en quina forma normal està, la resposta correcta comença per "depèn de quines regles regeixen aquestes columnes".
Saltar-se nivells en comprovar. És temptador anar directe a la 3FN perquè sembla la més important. Però si la taula té una llista separada per comes, ni tan sols està en 1FN, i parlar de dependències transitives sobre ella no té sentit. Comprova de baix a dalt i corregeix abans de pujar.
Buscar dependències parcials en taules amb clau simple. És temps perdut: no en poden existir. Si la clau és un sol atribut, la taula està en 2FN per construcció. Aquesta drecera estalvia la meitat de la feina en un esquema amb claus subrogades.
Confondre "molts valors" amb "no atòmic". Que un soci tingui tres telèfons no viola la 1FN; el que la viola és ficar-los a la mateixa cel·la o en tres columnes. La taula telefons_soci amb tres files és perfectament 1FN: cada cel·la té un valor.
Aplicar 1FN a ultrança contra els tipus compostos moderns. Una columna JSONB amb dades que sempre es llegeixen senceres, sense regles de negoci sobre els seus camps, és acceptable. La discussió útil no és "això és atòmic?" sinó "necessitaré filtrar, agrupar o restringir per una part d'això?".
Descompondre a FNBC sense comprovar quines dependències es perden. És l'error més car d'aquesta lliçó, perquè el resultat sembla millor: menys redundància, taules més netes. I tanmateix una regla de negoci ha deixat d'estar garantida. Abans de descompondre a FNBC, fes la llista de dependències originals i comprova una a una en quina taula queda cadascuna. Si alguna no queda enlloc, decideix conscientment entre quedar-te en 3FN o afegir un activador.
Perseguir la 5FN. Si et trobes raonant sobre dependències de reunió en un projecte normal, gairebé segur que has diagnosticat malament alguna cosa més avall. Torna a comprovar la 3FN.
Consell de mètode: quan revisis un esquema existent, fes-ho taula per taula i escriu per a cadascuna tres línies: les seves dependències, les seves claus candidates i la seva forma normal més alta. És un document de mitja hora que serveix durant anys i que converteix les discussions de disseny en discussions amb dades.
Exercicis
Exercici 1: Diagnosticar la forma normal més alta
Per a cadascuna d'aquestes tres taules de BiblioRed, determina la forma normal més alta que compleix i, si no arriba a 3FN, indica quina dependència ho impedeix i quina anomalia produeix.
a) reserves_v0(soci_id, material_id, data_reserva, soci_email, estat)
Regles: un soci només pot tenir una reserva viva per material; el correu identifica el soci.
b) multes_v0(multa_id, soci_id, motiu, tarifa_dia, dies_retard, import)
Regles: cada motiu té una tarifa diària fixa establerta per ordenança (retard = 0,10 €/dia, deteriorament = 2,00 €/dia); l'import és la tarifa pels dies.
c) subtitols_dvd(material_id, idioma)
Regles: un DVD pot tenir subtítols en diversos idiomes; no hi ha més regles.
Exercici 2: 3FN sí, FNBC no
BiblioRed assigna a cada soci un bibliotecari de referència amb aquesta taula:
referencies(soci_id, especialitat, bibliotecari_id)
Regles de negoci:
- P1: per a cada soci i cada especialitat (infantil, literatura, tècnica) hi ha un únic bibliotecari de referència.
- P2: cada bibliotecari està especialitzat en una única especialitat.
Es demana:
- a) Escriure les dependències funcionals.
- b) Trobar totes les claus candidates calculant clausures.
- c) Demostrar que la taula està en 3FN però no en FNBC.
- d) Proposar la descomposició a FNBC i dir quina dependència es perd.
Exercici 3: 4FN o relació ternària?
Per a cadascun d'aquests dos casos, digues si la taula de tres columnes viola la 4FN (i cal separar-la en dues) o si representa una relació ternària legítima (i cal deixar-la com està). Justifica-ho amb la pregunta de diagnòstic de la secció 9.
a) esdeveniment_idiomes_accessibilitat(esdeveniment_id, idioma, servei_accessibilitat)
Un esdeveniment s'ofereix en diversos idiomes (català, castellà) i disposa de diversos serveis d'accessibilitat (bucle magnètic, intèrpret de signes, subtitulat en directe). Els serveis estan disponibles per a tot l'esdeveniment, independentment de l'idioma.
b) participacions(esdeveniment_id, ponent_id, rol)
A l'esquema real de BiblioRed: un esdeveniment té diversos ponents, i cada ponent desenvolupa un o diversos rols en aquell esdeveniment concret. Que el ponent 7 sigui moderador a l'esdeveniment 210 no diu res sobre què fa al 211.
Solucions
Solució 1
a) reserves_v0 està en 1FN, no arriba a 2FN.
Dependències:
La clau candidata és {soci_id, material_id} (la regla "una reserva viva per soci i material"). Atributs primers: soci_id, material_id.
r2 és una dependència parcial: soci_id és mitja clau i ja determina soci_email, que és no primer. Per tant la taula no està en 2FN, i la forma normal més alta que compleix és la 1FN. Aquest és un bon recordatori de per què es comprova de baix a dalt: qui vagi directe a buscar dependències transitives es trobarà que la pregunta ni tan sols escau.
L'anomalia és d'actualització: si la Marta Alsina corregeix el seu correu, cal tocar totes les seves reserves, i n'hi ha prou d'oblidar-ne una per tenir dos correus per a la mateixa persona —exactament el defecte 5 del diagnòstic de 01-01. La correcció és treure soci_email d'aquí: ja viu a socis, i des de reserves s'hi arriba per la clau forana soci_id.
b) multes_v0 està en 2FN, no en 3FN.
Dependències:
m1: multa_id → {soci_id, motiu, dies_retard}
m2: motiu → tarifa_dia
m3: {tarifa_dia, dies_retard} → importLa clau candidata és {multa_id}, d'un sol atribut, així que la 2FN està garantida.
m2 viola la 3FN: motiu no és superclau ({motiu}⁺ = {motiu, tarifa_dia}) i tarifa_dia no és primer. És una dependència transitiva multa_id → motiu → tarifa_dia. L'anomalia: si l'ordenança apuja la tarifa de retard a 0,15 €/dia, cal actualitzar totes les multes de retard de l'històric —i això, a més de costós, és incorrecte, perquè les multes ja emeses es van calcular amb la tarifa antiga.
La correcció és una taula tarifes_multa(motiu, tarifa_dia) amb motiu com a clau primària i una clau forana des de multes.
m3 també és una dependència transitiva (import és un valor derivat). Aquí la resposta correcta no és evident i és una avançada perfecta de la lliçó següent: l'import s'ha de quedar a multes perquè és una dada històrica que ha de romandre congelada amb el valor que va tenir el dia de l'emissió, encara que la tarifa canviï després. És una desnormalització deliberada i justificada, no un descuit.
c) subtitols_dvd està en FNBC (i en 4FN, i en 5FN).
L'única dependència no trivial possible seria entre material_id i idioma, i no existeix en cap direcció: un DVD té diversos idiomes i un idioma és a diversos DVD. La clau candidata és la taula sencera, {material_id, idioma}; tots dos atributs són primers; no hi ha cap determinant que no sigui superclau. Tota taula d'exactament dos atributs la clau de la qual és la parella està en FNBC per construcció. És un exemple que les taules d'associació pura són les més "normals" que existeixen.
Solució 2
a) Dependències:
b) Claus candidates.
{soci_id, especialitat}⁺: perp1hi entrabibliotecari_id. Ja hi són tots tres. Superclau. Mínima?{soci_id}⁺ = {soci_id}(cap dependència no arrenca només ambsoci_id);{especialitat}⁺ = {especialitat}. Cap meitat no basta. Clau candidata 1.{soci_id, bibliotecari_id}⁺: perp2hi entraespecialitat. Tots tres. Superclau. Mínima?{bibliotecari_id}⁺ = {bibliotecari_id, especialitat}, no contésoci_id. I{soci_id}⁺ja hem vist que no creix. Clau candidata 2.
Dues claus candidates, compostes i encavalcades en soci_id. Atributs primers: tots tres (soci_id a totes dues, especialitat a la 1, bibliotecari_id a la 2). No hi ha atributs no primers.
c) Està en 3FN. La 3FN exigeix, per a cada dependència no trivial X → A, que X sigui superclau o que A sigui primer:
p1:{soci_id, especialitat}és superclau. Compleix.p2:bibliotecari_idno és superclau, peròespecialitatés primer (és a la clau candidata 1). Compleix per la segona via.
No està en FNBC, perquè p2 té un determinant, bibliotecari_id, que no és superclau, i la FNBC no admet l'excusa que el determinat sigui primer.
El mal concret: l'especialitat de cada bibliotecari està repetida a totes les files de socis que el tenen assignat. Si el bibliotecari 22 canvia d'especialitat, cal actualitzar centenars de files. I no es pot registrar l'especialitat d'un bibliotecari acabat de contractar fins que se li assigni algun soci.
d) Descomposició a FNBC: es treu la dependència infractora.
CREATE TABLE bibliotecaris_especialitat (
bibliotecari_id INTEGER NOT NULL,
especialitat VARCHAR(20) NOT NULL,
CONSTRAINT pk_biblio_esp PRIMARY KEY (bibliotecari_id)
);
CREATE TABLE referencies (
soci_id INTEGER NOT NULL,
bibliotecari_id INTEGER NOT NULL,
CONSTRAINT pk_referencies PRIMARY KEY (soci_id, bibliotecari_id)
);Es perd p1: {soci_id, especialitat} → bibliotecari_id. Ni bibliotecaris_especialitat ni referencies no contenen alhora soci_id i especialitat, així que la regla P1 ("un sol bibliotecari per soci i especialitat") ja no la pot garantir cap restricció declarativa. Res no impedeix assignar a la Marta Alsina dos bibliotecaris que resultin ser tots dos de literatura.
La decisió és la que vam plantejar a la secció 8: quedar-se en 3FN i garantir P1 amb la clau primària {soci_id, especialitat}, acceptant la redundància de l'especialitat; o descompondre a FNBC i garantir P1 amb un activador. Si els bibliotecaris canvien d'especialitat molt rarament —l'habitual— la primera opció és més assenyada.
Solució 3
a) Viola la 4FN. Apliquem la pregunta de diagnòstic: canvia el conjunt de serveis d'accessibilitat d'un esdeveniment segons l'idioma? L'enunciat diu explícitament que no: els serveis estan disponibles per a tot l'esdeveniment. Són dues llistes independents que pengen del mateix esdeveniment_id, i hi ha dues dependències multivaluades:
Un esdeveniment en 2 idiomes amb 3 serveis generaria 6 files per a 5 fets, i afegir un quart servei obligaria a inserir 2 files. La correcció és separar en esdeveniments_idiomes(esdeveniment_id, idioma) i esdeveniments_accessibilitat(esdeveniment_id, servei).
b) No viola la 4FN: és una relació ternària legítima. Mateixa pregunta: canvia el conjunt de rols segons l'esdeveniment? Sí, rotundament: l'enunciat diu que el rol del ponent 7 a l'esdeveniment 210 no diu res sobre el que fa al 211. El rol és un atribut de la participació concreta, no una propietat independent ni de l'esdeveniment ni del ponent.
Formalment, no existeix esdeveniment_id ↠ rol independent de ponent_id, així que no hi ha cap dependència multivaluada per violar. Separar aquesta taula seria un error greu: perdria la informació de qui fa què, que és precisament el que es vol desar. És exactament el cas que vam resoldre amb la regla 9 de transformació a 04-03.
La diferència entre els dos casos és la lliçó que cal endur-se: l'estructura de la taula no diu si viola la 4FN; ho diu la regla de negoci. Dues taules amb tres columnes cadascuna, una s'ha de partir i l'altra no.
Conclusió
Ja tens el catàleg complet, i amb ell la capacitat d'emetre un judici verificable sobre qualsevol taula.
La 1FN exigeix valors atòmics: ni llistes dins d'una cel·la, ni columnes numerades, ni files duplicades. És la que converteix els antipatrons que vam denunciar a 04-01 en violacions amb nom, i la seva correcció és la regla 3 de transformació que ja vam aplicar a 04-03. Vam aprendre també que "atòmic" no és una propietat de la dada sinó del seu ús: una data ho és, un JSONB que només es llegeix sencer ho és a la pràctica, i una adreça ho és fins al dia en què cal agrupar per codi postal.
La 2FN elimina les dependències parcials, que només poden existir en taules amb clau composta. La 3FN elimina les transitives, en què un atribut no primer depèn d'un altre no primer. Totes dues són obligatòries i totes dues es corregeixen igual: es treu la dependència infractora a una taula pròpia, amb el determinant com a clau primària i una clau forana a l'original.
La FNBC tanca l'escletxa que deixa la 3FN amb una definició d'una sola línia —tot determinant és superclau— i només se'n distingeix quan hi ha claus candidates compostes i encavalcades. La seva lletra petita és fonamental: la descomposició a FNBC pot perdre la conservació de dependències, i això significa que una regla de negoci deixa de poder-se garantir dins d'una sola taula. Es decideix amb criteri, no automàticament.
La 4FN ataca un problema diferent: dues llistes independents ficades a la mateixa taula, que es multipliquen entre si sense significar res. El seu remei —una taula per llista— és el que el mòdul 4 ja havia aplicat per intuïció a participacions i esdeveniments_materials. La 5FN i la FNDC existeixen, són coherents, i en un sistema com BiblioRed no s'apliquen; convé saber que hi són i no perseguir-les.
I per damunt de tot el catàleg, el criteri de la indústria: fins a 3FN/FNBC sempre, més enllà només si el cas ho demana. No per mandra, sinó perquè el rendiment decreixent és brusc i perquè un esquema en 3FN és un esquema que la següent persona entendrà.
El que no hem fet encara és aplicar res d'això de principi a fi sobre una taula real. Hem vist sis exemples mínims, cadascun construït per il·lustrar una forma normal aïllada. La realitat no arriba així: arriba com el full de càlcul de BiblioRed, amb tretze columnes, onze dependències, valors no atòmics, dependències parcials i transitives alhora, vuitanta-quatre mil files de dades històriques que cal migrar i netejar, i un sistema en producció que no es pot aturar.
A la lliçó 05-03, Procés de Normalització, fem exactament això. Un procediment de sis passos, aplicat de principi a fi sobre prestecs_full: d'una taula plana sense normalitzar fins a 3FN, mostrant a cada pas les dades abans i després i el SQL que descompon i migra. I amb això, les dues propietats que tota descomposició ha de complir —sense pèrdua d'informació i conservació de les dependències—, inclòs el contraexemple d'una descomposició mal feta que inventa files en reunir. Acabarem sotmetent a examen l'esquema ampliat del mòdul 4, taula per taula, per veure si aguanta.
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
