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

  1. Què significa que una relació "està en" una forma normal
  2. Per què les formes normals són acumulatives
  3. Primera forma normal (1FN): valors atòmics
  4. Què significa exactament "atòmic"
  5. Segona forma normal (2FN): sense dependències parcials
  6. Tercera forma normal (3FN): sense dependències transitives
  7. Forma normal de Boyce-Codd (FNBC): tot determinant és superclau
  8. Quan la descomposició a FNBC no conserva les dependències
  9. Quarta forma normal (4FN): dependències multivaluades
  10. Cinquena forma normal (5FN): dependències de reunió
  11. La forma normal de domini i clau (FNDC): el límit teòric
  12. Taula resum de totes les formes normals
  13. El criteri real de la indústria

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

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

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

-- Buscar el soci amb el telèfon 938880011
SELECT * FROM socis_full WHERE telefons = '938880011';
 soci_id | nom | telefons
---------+-----+----------
(0 rows)

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:

SELECT * FROM socis_full WHERE telefons LIKE '%938880011%';

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.

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

  1. 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}   ← PARCIAL

Hi 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;
UPDATE 2
-- 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');
ERROR:  null value in column "prestec_id" of relation "prestec_linies" violates not-null constraint

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

  1. 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 → A que 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:

e1: sucursal_id → {nom, carrer, cp, ciutat}
e2: cp → ciutat                                ← TRANSITIVA

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 cp superclau? {cp}⁺ = {cp, ciutat}. No conté tots els atributs. No.
  • És ciutat primer? L'única clau candidata és {sucursal_id}, i ciutat no 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

  1. 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 → Y que 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:

h1: {esdeveniment_id, rol} → ponent_id       (de N1)
h2: ponent_id → rol                          (de N2)

Claus candidates. Calculem clausures:

  • {esdeveniment_id, rol}⁺ = {esdeveniment_id, rol, ponent_id} = tots. Superclau. Mínima (ni esdeveniment_id ni rol no basten tots sols). Clau candidata 1.
  • {esdeveniment_id, ponent_id}⁺: per h2 hi entra rol, 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 = rol sí 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_id no é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.

  1. 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_rol no hi és: no hi ha esdeveniment_id.
  • A assignacions no hi és: no hi ha rol.

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 moderador

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

  1. 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;
DELETE 3

La dependència multivaluada

Definició. En una relació R, hi ha una dependència multivaluada de Y respecte de X, escrita X ↠ Y (amb doble punta de fletxa), si el conjunt de valors de Y associats a un valor de X depèn només de X i és independent dels altres atributs de la relació.

Es llegeix "X multidetermina Y". En el nostre cas:

esdeveniment_id ↠ ponent_id
esdeveniment_id ↠ material_id

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

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

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

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

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

r1: {soci_id, material_id} → {data_reserva, estat}
r2: soci_id → soci_email

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} → import

La 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:

p1: {soci_id, especialitat} → bibliotecari_id      (de P1)
p2: bibliotecari_id → especialitat                 (de P2)

b) Claus candidates.

  • {soci_id, especialitat}⁺: per p1 hi entra bibliotecari_id. Ja hi són tots tres. Superclau. Mínima? {soci_id}⁺ = {soci_id} (cap dependència no arrenca només amb soci_id); {especialitat}⁺ = {especialitat}. Cap meitat no basta. Clau candidata 1.
  • {soci_id, bibliotecari_id}⁺: per p2 hi entra especialitat. 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_id no é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:

esdeveniment_id ↠ idioma
esdeveniment_id ↠ servei_accessibilitat

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.

© Copyright 2026. Tots els drets reservats