La lección anterior construyó el instrumental: dependencias funcionales, sus tipos, los axiomas de Armstrong, el cierre X⁺, las claves candidatas y la distinción entre atributos primos y no primos. Todo eso existía para un único propósito, y es el de esta lección: poder enunciar con precisión qué es una tabla bien diseñada.
Una forma normal es una condición que una relación cumple o no cumple. No es un consejo ni una buena práctica: es una propiedad verificable, como decir que un número es par. Cada forma normal prohíbe un tipo concreto de dependencia mal colocada, y las prohibiciones se apilan: cada nivel exige lo del anterior y algo más.
Esta lección es el catálogo. Recorreremos las seis formas normales que se usan —primera, segunda, tercera, Boyce-Codd, cuarta y quinta— más una séptima que solo tiene interés teórico. De cada una veremos cuatro cosas: la definición formal, un ejemplo mínimo de BiblioRed que la viola con datos concretos, la corrección, y el motivo por el que importa. Lo que no haremos aquí es aplicar una metodología completa sobre una tabla real: eso es la lección 05-03. Aquí se trata de tener el catálogo entero antes de empezar a usarlo, igual que se aprenden las herramientas antes de la obra.
Contenido
- Qué significa que una relación "está en" una forma normal
- Por qué las formas normales son acumulativas
- Primera forma normal (1FN): valores atómicos
- Qué significa exactamente "atómico"
- Segunda forma normal (2FN): sin dependencias parciales
- Tercera forma normal (3FN): sin dependencias transitivas
- Forma normal de Boyce-Codd (FNBC): todo determinante es superclave
- Cuando la descomposición a FNBC no conserva las dependencias
- Cuarta forma normal (4FN): dependencias multivaluadas
- Quinta forma normal (5FN): dependencias de reunión
- La forma normal de dominio y clave (FNDC): el límite teórico
- Tabla resumen de todas las formas normales
- El criterio real de la industria
- Qué significa que una relación "está en" una forma normal
Empecemos por lo básico, porque el lenguaje que se usa aquí confunde a mucha gente.
Cuando se dice que "la tabla socios está en tercera forma normal", se está afirmando algo sobre la estructura de la tabla y sus dependencias funcionales, no sobre los datos que contiene hoy. Es una propiedad del diseño. Si mañana insertas mil filas más, la tabla sigue estando en tercera forma normal; si cambias su estructura o descubres una regla de negocio nueva, puede dejar de estarlo.
Tres precisiones importantes:
Las formas normales se predican de relaciones, no de bases de datos. No existe "la base de datos de BiblioRed está en 3FN" como afirmación única: cada tabla se evalúa por separado. Lo que sí se dice, por abreviar, es que un esquema está en 3FN cuando todas sus tablas lo están.
Hacen falta las dependencias funcionales para responder. Mirando solo el CREATE TABLE no se puede saber en qué forma normal está una tabla, porque las formas normales hablan de dependencias, y las dependencias vienen del negocio. Dos tablas con columnas idénticas pueden estar una en 3FN y otra no, si las reglas que las rigen son distintas. Esto sorprende, pero es la consecuencia directa de lo que vimos en la sección 6 de 05-01.
El adjetivo "normal" no significa "habitual". Viene del vocabulario matemático, donde "forma normal" designa una forma canónica a la que se lleva un objeto para poder compararlo con otros. No tiene nada que ver con normalidad estadística.
- Por qué las formas normales son acumulativas
Las formas normales están ordenadas y cada una incluye a la anterior: para estar en 3FN hay que estar antes en 2FN, y para estar en 2FN hay que estar antes en 1FN. No es una convención arbitraria: está en la propia definición de cada forma, que empieza literalmente diciendo "una relación está en nFN si está en (n−1)FN y además…".
flowchart TD
R["Relación cualquiera<br/><i>(puede tener listas en las celdas)</i>"]
N1["<b>1FN</b><br/>Valores atómicos"]
N2["<b>2FN</b><br/>Sin dependencias parciales"]
N3["<b>3FN</b><br/>Sin dependencias transitivas"]
BC["<b>FNBC</b><br/>Todo determinante es superclave"]
N4["<b>4FN</b><br/>Sin dependencias multivaluadas"]
N5["<b>5FN</b><br/>Sin dependencias de reunión"]
DK["<b>FNDC</b><br/>Solo dominios y claves"]
R --> N1 --> N2 --> N3 --> BC --> N4 --> N5 --> DK
style N3 fill:#2d6a4f,color:#fff
style BC fill:#2d6a4f,color:#fff
Léelo como una escalera de exigencia: cuanto más arriba, más restrictiva es la condición y menos tablas la cumplen. Los dos escalones resaltados son el objetivo práctico habitual, y en la sección 13 explicaremos por qué.
La consecuencia lógica de la acumulación es útil en los dos sentidos:
- Si una tabla no está en 2FN, tampoco está en 3FN ni en ninguna superior. Basta un fallo abajo para descartarlo todo.
- Si una tabla sí está en FNBC, automáticamente está en 3FN, 2FN y 1FN. No hace falta comprobarlas.
Por eso las comprobaciones se hacen de abajo arriba, y en cuanto una falla se corrige antes de seguir subiendo. Es exactamente el orden que seguirá el proceso de la lección 05-03.
Una advertencia de nomenclatura antes de seguir: en la bibliografía en inglés y en algunas lecciones anteriores de este curso verás 1NF, 2NF, 3NF y BCNF (first normal form, Boyce-Codd normal form). Son exactamente lo mismo que 1FN, 2FN, 3FN y FNBC. Usaremos la nomenclatura española.
- Primera forma normal (1FN): valores atómicos
Definición. Una relación está en primera forma normal si el valor de cada atributo en cada fila es atómico: un solo valor indivisible del dominio del atributo. En particular, no hay listas dentro de una celda, ni grupos de columnas repetidas, ni filas duplicadas.
Es la forma normal más básica y también la más malinterpretada. Vamos con un ejemplo de BiblioRed que ya conocemos por otro camino.
Violación 1: la lista dentro de la celda
La lección 04-01 denunció el anti-patrón de la "lista separada por comas" y la 04-03 lo resolvió con la regla 3 de transformación. Ahora podemos decir formalmente qué estaba mal: violaba la primera forma normal.
Así estaba la tabla de socios en la hoja original de BiblioRed:
socios_hoja — no está en 1FN
| socio_id | nombre | telefonos |
|---|---|---|
| 14 | Marta Alsina | 600111222, 938880011 |
| 15 | Iván Pereda | 600333444 |
| 16 | Nuria Bastos | 600555666, 938880033, 617220099 |
El valor 600111222, 938880011 no es un teléfono: es dos teléfonos metidos en una cadena de texto. El SGBD ve una cadena y la trata como tal, con consecuencias muy concretas:
-- Buscar al socio con el teléfono 938880011
SELECT * FROM socios_hoja WHERE telefonos = '938880011';No lo encuentra, porque el valor guardado es '600111222, 938880011', que no es igual a '938880011'. La salida habitual es el LIKE, y es peor que el problema:
Eso funciona por casualidad y falla en cuanto un número contenga a otro como subcadena. Además: no se puede poner una restricción de formato sobre un teléfono individual, no se puede contar cuántos teléfonos hay sin trocear cadenas, no se puede indexar, y no se puede impedir que el mismo número se repita.
Corrección: la regla 3 del módulo 4. Una tabla nueva con la clave ajena y el valor, y la clave primaria formada por ambos:
CREATE TABLE telefonos_socio (
socio_id INTEGER NOT NULL,
numero VARCHAR(20) NOT NULL,
tipo VARCHAR(10) NOT NULL DEFAULT 'movil',
CONSTRAINT pk_telefonos_socio PRIMARY KEY (socio_id, numero),
CONSTRAINT fk_telefonos_socio FOREIGN KEY (socio_id)
REFERENCES socios (socio_id) ON DELETE CASCADE ON UPDATE CASCADE
);telefonos_socio — en 1FN
| socio_id | numero | tipo |
|---|---|---|
| 14 | 600111222 | movil |
| 14 | 938880011 | fijo |
| 15 | 600333444 | movil |
| 16 | 600555666 | movil |
| 16 | 938880033 | fijo |
| 16 | 617220099 | trabajo |
Ahora cada celda tiene un valor, la búsqueda por igualdad funciona, el CHECK de formato se puede aplicar número a número, y la clave primaria compuesta impide repetir un teléfono en el mismo socio. Es la tabla que ya está en el esquema de BiblioRed desde la lección 04-03; lo nuevo es saber que su justificación formal se llama primera forma normal.
Exactamente el mismo caso, con el mismo remedio, es el de los idiomas de subtítulos de un DVD. Guardar subtitulos = 'es,ca,en' en materiales_dvd es una violación de 1FN, y la solución es subtitulos_dvd(material_id, idioma), con idioma sobre un dominio validado. Otra vez: la tabla ya existía; ahora sabemos su nombre formal.
Violación 2: el grupo repetitivo
La segunda forma de romper la 1FN es más sutil porque cada celda sí tiene un valor único. El problema es la estructura de columnas:
socios_hoja_v2 — tampoco está en 1FN
| socio_id | nombre | telefono_1 | telefono_2 | telefono_3 |
|---|---|---|---|---|
| 14 | Marta Alsina | 600111222 | 938880011 | (NULL) |
| 15 | Iván Pereda | 600333444 | (NULL) | (NULL) |
| 16 | Nuria Bastos | 600555666 | 938880033 | 617220099 |
Es el anti-patrón de las "columnas numeradas" de 04-01. Cada celda es atómica, sí, pero el grupo telefono_1, telefono_2, telefono_3 es un atributo multivaluado disfrazado de tres columnas, y las tres columnas no son tres atributos distintos: son tres ocurrencias del mismo. Los síntomas lo delatan:
-- Buscar al socio con el teléfono 938880011: hay que mirar en las tres.
SELECT * FROM socios_hoja_v2
WHERE telefono_1 = '938880011'
OR telefono_2 = '938880011'
OR telefono_3 = '938880011';Y si mañana un socio tiene cuatro teléfonos, hay que hacer un ALTER TABLE —cambiar el esquema por un cambio de datos— y reescribir todas las consultas. La corrección es la misma tabla telefonos_socio de antes.
Un matiz: las filas duplicadas
La definición clásica de relación (la de 02-01) dice que una relación es un conjunto de tuplas, y en un conjunto no hay elementos repetidos. Por tanto, en teoría estricta, dos filas idénticas violan la 1FN.
En la práctica, SQL permite tablas sin clave primaria y con filas duplicadas. La recomendación operativa es inequívoca y ya la seguimos desde el módulo 2: toda tabla debe tener clave primaria declarada. Con eso, las filas duplicadas son imposibles y este aspecto de la 1FN queda garantizado por el SGBD.
- Qué significa exactamente "atómico"
Aquí hay que ser honesto, porque es donde la 1FN genera más discusiones estériles.
"Atómico" no es una propiedad del dato: es una propiedad de la relación entre el dato y el uso que se le da. Un valor es atómico si la aplicación nunca necesita mirar dentro de él para hacer su trabajo.
Mira estos cuatro casos de BiblioRed:
| Valor | ¿Atómico? | Por qué |
|---|---|---|
'Carrer Major, 12, 08110 Vallmar' en una columna direccion |
Depende | Si solo se imprime en una etiqueta, sí. Si hay que agrupar préstamos por código postal, no: el CP hay que extraerlo con funciones de texto, y entonces la dirección debía estar descompuesta |
'2026-04-09' en una columna DATE |
Sí | Aunque contiene año, mes y día, el tipo DATE los expone como funciones (EXTRACT(YEAR FROM …)) sin trocear texto. El SGBD entiende la estructura interna |
'600111222, 938880011' en telefonos |
No | Hay que partir la cadena para usar cualquiera de los dos, y no hay ningún tipo que le dé sentido a la coma |
'{"p1": 4, "p2": 5}' en una columna JSONB de informes_evento |
Sí, en la práctica | El SGBD tiene operadores nativos (->, @>), índices GIN y validación de estructura. No estás partiendo texto: estás consultando un tipo compuesto que PostgreSQL entiende |
El caso del JSONB merece un párrafo, porque es donde la 1FN de 1970 se encuentra con las bases de datos de hoy. Los puristas dirían que una columna JSONB con varios valores dentro viola la 1FN. En la práctica se acepta cuando se cumplen dos condiciones: el contenido no participa en ninguna relación con otras tablas (no hay claves ajenas hacia dentro del JSON) y no hay reglas de negocio que dependan de sus campos individuales. En BiblioRed, informes_evento.respuestas_encuesta cumple ambas: son respuestas libres a un cuestionario que solo se leen enteras para generar un informe. Si mañana hiciera falta agregar por pregunta, calcular medias por ítem o poner restricciones, dejaría de estar justificado y habría que sacarlo a una tabla respuestas_encuesta(evento_id, socio_id, pregunta, valor).
Esta discusión ya la tuvimos con otro vocabulario en 03-04, al hablar de jsonb en PostgreSQL. La regla resumida:
Guarda un valor compuesto solo si lo vas a usar siempre entero. En cuanto necesites buscar, filtrar, agrupar o restringir por una de sus partes, esa parte tiene que ser una columna o una fila.
- Segunda forma normal (2FN): sin dependencias parciales
Definición. Una relación está en segunda forma normal si está en 1FN y, además, todo atributo no primo depende funcionalmente de la clave candidata completa, y no de una parte de ella. Dicho de otro modo: no existen dependencias parciales de atributos no primos respecto de ninguna clave candidata.
Recordemos el vocabulario de 05-01: un atributo no primo es el que no forma parte de ninguna clave candidata, y una dependencia es parcial cuando un subconjunto propio de la clave ya determina el atributo.
De la definición se sigue un atajo enorme:
Si todas las claves candidatas son de un solo atributo, la relación está automáticamente en 2FN. No hay "partes" de una clave de un solo atributo, así que no puede haber dependencias parciales.
Por eso la 2FN solo es un problema en tablas con clave compuesta: tablas de unión, entidades débiles, y tablas planas heredadas de hojas de cálculo.
Ejemplo mínimo: el detalle de préstamos
BiblioRed evaluó en su día permitir que un socio se llevara varios ejemplares en una sola operación de mostrador, con un "préstamo" que agrupa varias líneas. Alguien propuso esta tabla:
prestamo_lineas — en 1FN pero NO en 2FN
Clave candidata: {prestamo_id, ejemplar_id} (un ejemplar aparece una sola vez en un préstamo).
| prestamo_id | ejemplar_id | fecha_prestamo | socio_id | ejemplar_estado | ejemplar_sucursal |
|---|---|---|---|---|---|
| 5001 | 3081 | 2026-04-09 | 14 | prestado | Norte |
| 5001 | 3082 | 2026-04-09 | 14 | prestado | Norte |
| 5001 | 3090 | 2026-04-09 | 14 | prestado | Norte |
| 5002 | 3095 | 2026-04-10 | 16 | prestado | Sur |
| 5003 | 3081 | 2026-05-02 | 15 | prestado | Norte |
Las dependencias, según las reglas de negocio:
d1: {prestamo_id, ejemplar_id} → (nada exclusivo suyo)
d2: prestamo_id → {fecha_prestamo, socio_id} ← PARCIAL
d3: ejemplar_id → {ejemplar_estado, ejemplar_sucursal} ← PARCIALHay dos dependencias parciales, una por cada mitad de la clave. Y sus consecuencias son visibles en la tabla de arriba: 2026-04-09 y 14 están escritos tres veces porque el préstamo 5001 tiene tres líneas; Norte está escrito tres veces para el ejemplar 3081 porque ese ejemplar aparece en dos préstamos.
Las anomalías correspondientes son las de siempre:
-- Anomalía de actualización: el ejemplar 3081 se traslada a la sucursal Centro.
-- Hay que tocar TODAS las líneas donde aparece, en todos los préstamos históricos.
UPDATE prestamo_lineas SET ejemplar_sucursal = 'Centro' WHERE ejemplar_id = 3081;-- Anomalía de inserción: no se puede registrar un ejemplar nuevo
-- que aún no se ha prestado nunca.
INSERT INTO prestamo_lineas (ejemplar_id, ejemplar_estado, ejemplar_sucursal)
VALUES (3096, 'disponible', 'Este');Corrección: cada dependencia parcial se saca a su propia tabla, con la parte de la clave que la determina como clave primaria.
-- Lo que depende de prestamo_id solo
CREATE TABLE prestamos (
prestamo_id INTEGER GENERATED BY DEFAULT AS IDENTITY,
fecha_prestamo DATE NOT NULL,
socio_id INTEGER NOT NULL,
CONSTRAINT pk_prestamos PRIMARY KEY (prestamo_id),
CONSTRAINT fk_prestamos_socio FOREIGN KEY (socio_id) REFERENCES socios (socio_id)
);
-- Lo que depende de ejemplar_id solo
CREATE TABLE ejemplares (
ejemplar_id INTEGER GENERATED BY DEFAULT AS IDENTITY,
estado VARCHAR(20) NOT NULL,
sucursal_id INTEGER NOT NULL,
CONSTRAINT pk_ejemplares PRIMARY KEY (ejemplar_id),
CONSTRAINT fk_ejemplares_sucursal FOREIGN KEY (sucursal_id)
REFERENCES sucursales (sucursal_id)
);
-- Lo que depende de la clave completa: solo la asociación
CREATE TABLE prestamo_lineas (
prestamo_id INTEGER NOT NULL,
ejemplar_id INTEGER NOT NULL,
CONSTRAINT pk_prestamo_lineas PRIMARY KEY (prestamo_id, ejemplar_id),
CONSTRAINT fk_pl_prestamo FOREIGN KEY (prestamo_id) REFERENCES prestamos (prestamo_id),
CONSTRAINT fk_pl_ejemplar FOREIGN KEY (ejemplar_id) REFERENCES ejemplares (ejemplar_id)
);Resultado, con los mismos datos:
prestamos
| prestamo_id | fecha_prestamo | socio_id |
|---|---|---|
| 5001 | 2026-04-09 | 14 |
| 5002 | 2026-04-10 | 16 |
| 5003 | 2026-05-02 | 15 |
ejemplares
| ejemplar_id | estado | sucursal_id |
|---|---|---|
| 3081 | prestado | 2 |
| 3082 | prestado | 2 |
| 3090 | prestado | 2 |
| 3095 | prestado | 3 |
prestamo_lineas
| prestamo_id | ejemplar_id |
|---|---|
| 5001 | 3081 |
| 5001 | 3082 |
| 5001 | 3090 |
| 5002 | 3095 |
| 5003 | 3081 |
Cuenta las repeticiones: la fecha 2026-04-09 aparece una vez en lugar de tres. La sucursal del ejemplar 3081 aparece una vez en lugar de dos. Y ahora sí se puede dar de alta un ejemplar que nadie ha pedido todavía. Fíjate además en que prestamo_lineas se ha quedado con solo la clave: es una tabla de asociación pura, y eso es perfectamente correcto —significa que el único hecho que aporta es "este ejemplar formó parte de este préstamo".
- Tercera forma normal (3FN): sin dependencias transitivas
Definición. Una relación está en tercera forma normal si está en 2FN y, además, ningún atributo no primo depende transitivamente de ninguna clave candidata. Equivalentemente: para toda dependencia no trivial
X → Aque se cumpla en la relación, o bienXes superclave, o bienAes un atributo primo.
La segunda formulación es la que se usa para comprobar, porque es mecánica: recorres las dependencias una a una y a cada una le haces dos preguntas.
La idea intuitiva es la de siempre: un atributo no primo no debe depender de otro atributo no primo. Si lo hace, es que ese segundo atributo es en realidad la clave de otra entidad que se ha colado en la tabla.
Ejemplo mínimo: el código postal de la sucursal
Esta es la violación de 3FN de manual, y BiblioRed la tiene servida.
sucursales_v0 — en 2FN pero NO en 3FN
Clave candidata: {sucursal_id} (un solo atributo, así que la 2FN está garantizada).
| sucursal_id | nombre | calle | cp | ciudad |
|---|---|---|---|---|
| 1 | Centro | Plaça de la Vila, 3 | 08100 | Vallmar |
| 2 | Norte | Carrer Major, 12 | 08110 | Vallmar |
| 3 | Sur | Avinguda del Port, 45 | 08130 | Vallmar de Mar |
| 4 | Este | Carrer del Bosc, 8 | 08110 | Vallmar |
Dependencias:
La segunda sale de una regla del negocio real: un código postal pertenece a un solo municipio. Y produce una dependencia transitiva sucursal_id → cp → ciudad, con cp que no es superclave (dos sucursales comparten el 08110) y ciudad que no es primo.
Aplicando la formulación mecánica a cp → ciudad:
- ¿Es
cpsuperclave?{cp}⁺ = {cp, ciudad}. No contiene todos los atributos. No. - ¿Es
ciudadprimo? La única clave candidata es{sucursal_id}, yciudadno está en ella. No.
Las dos respuestas son "no", luego viola la 3FN.
Y la anomalía es inmediata:
-- El municipio de Vallmar de Mar se fusiona y pasa a llamarse Vallmar Marina.
-- Todas las sucursales del 08130 tienen que cambiar. Si alguna se escapa:
UPDATE sucursales_v0 SET ciudad = 'Vallmar Marina' WHERE sucursal_id = 3;
-- Ahora imagina que hubiera una sucursal 5 también en el 08130 y no se actualizara.
-- La base de datos afirmaría que el 08130 está en dos municipios distintos,
-- lo cual es una contradicción con la regla de negocio e2.
SELECT cp, COUNT(DISTINCT ciudad) FROM sucursales_v0 GROUP BY cp HAVING COUNT(DISTINCT ciudad) > 1;Corrección: sacar la dependencia transitiva a su propia tabla, con el determinante como clave primaria, y dejar en la original una clave ajena.
CREATE TABLE codigos_postales (
cp VARCHAR(5) NOT NULL,
ciudad VARCHAR(60) NOT NULL,
CONSTRAINT pk_codigos_postales PRIMARY KEY (cp)
);
CREATE TABLE sucursales (
sucursal_id INTEGER GENERATED BY DEFAULT AS IDENTITY,
nombre VARCHAR(60) NOT NULL,
calle VARCHAR(120) NOT NULL,
cp VARCHAR(5) NOT NULL,
CONSTRAINT pk_sucursales PRIMARY KEY (sucursal_id),
CONSTRAINT uq_sucursales_nombre UNIQUE (nombre),
CONSTRAINT fk_sucursales_cp FOREIGN KEY (cp)
REFERENCES codigos_postales (cp) ON UPDATE CASCADE
);codigos_postales
| cp | ciudad |
|---|---|
| 08100 | Vallmar |
| 08110 | Vallmar |
| 08130 | Vallmar de Mar |
sucursales
| sucursal_id | nombre | calle | cp |
|---|---|---|---|
| 1 | Centro | Plaça de la Vila, 3 | 08100 |
| 2 | Norte | Carrer Major, 12 | 08110 |
| 3 | Sur | Avinguda del Port, 45 | 08130 |
| 4 | Este | Carrer del Bosc, 8 | 08110 |
Ahora el municipio de cada código postal está escrito una vez. Cambiarlo es un UPDATE de una fila, y no hay ninguna manera física de que el 08110 aparezca en dos ciudades. Es la misma tabla sucursales que llevamos usando desde el módulo 2, ahora con su justificación formal.
(Nota práctica: en un sistema pequeño con cuatro sucursales, sacar una tabla de códigos postales puede parecer excesivo, y de hecho el esquema real de BiblioRed guarda dir_ciudad directamente en sucursales. Es una decisión defendible —una desnormalización consciente sobre una tabla de cuatro filas que casi nunca cambia—, pero conviene tomarla sabiendo que es una desnormalización, no por no haber visto la dependencia. Sobre cómo tomar esa decisión con criterio va toda la lección 05-04.)
La diferencia entre 2FN y 3FN, en una frase
Las dos prohíben que un atributo no primo dependa de algo que no sea la clave completa. Cambia de qué depende indebidamente:
| Depende indebidamente de… | Solo puede pasar si… | |
|---|---|---|
| 2FN | Una parte de la clave | La clave es compuesta |
| 3FN | Otro atributo no primo | Hay atributos no primos que determinan a otros |
- Forma normal de Boyce-Codd (FNBC): todo determinante es superclave
La tercera forma normal deja una rendija abierta. Su definición perdona una dependencia X → A si A resulta ser un atributo primo, aunque X no sea superclave. Raymond Boyce y Edgar Codd propusieron en 1974 cerrar esa rendija, y el resultado es una definición mucho más simple:
Definición. Una relación está en forma normal de Boyce-Codd si, para toda dependencia funcional no trivial
X → Yque se cumpla en ella,Xes superclave.
Sin excepciones, sin distinguir primos de no primos. Es la definición más limpia de todas las formas normales, y por eso muchos autores la enseñan antes que la 3FN.
Comparadas lado a lado:
| Forma | Para toda dependencia no trivial X → A… |
|---|---|
| 3FN | X es superclave o A es primo |
| FNBC | X es superclave |
FNBC es estrictamente más exigente. Toda relación en FNBC está en 3FN; lo contrario no siempre.
El caso clásico: en 3FN pero no en FNBC
Para que aparezca la diferencia hacen falta tres condiciones simultáneas, y por eso el caso es infrecuente: la relación debe tener varias claves candidatas, esas claves deben ser compuestas, y deben solaparse (compartir algún atributo).
Construyámoslo en BiblioRed. Supongamos —y esto es una hipótesis para el ejemplo, no la regla real del esquema del módulo 4— que BiblioRed impone dos normas al asignar ponentes a los eventos:
- N1: en un evento, cada rol lo desempeña una sola persona. No hay dos moderadores en la misma charla.
- N2: cada ponente del registro tiene un rol fijo asignado por contrato. Clara Ferrán es siempre moderadora; nunca hace de tallerista.
asignaciones — en 3FN pero NO en FNBC
| evento_id | ponente_id | rol |
|---|---|---|
| 210 | 7 | moderador |
| 210 | 9 | tallerista |
| 211 | 7 | moderador |
| 211 | 12 | autor_invitado |
| 212 | 9 | tallerista |
Las dependencias que salen de N1 y N2:
Claves candidatas. Calculemos cierres:
{evento_id, rol}⁺={evento_id, rol, ponente_id}= todos. Superclave. Mínima (nievento_idnirolbastan solos). Clave candidata 1.{evento_id, ponente_id}⁺: porh2entrarol, y ya tenemos todos. Superclave. Mínima. Clave candidata 2.
Dos claves candidatas, compuestas, y solapadas: ambas contienen evento_id. Se cumplen las tres condiciones.
Atributos primos: evento_id (en las dos), rol (en la 1), ponente_id (en la 2). Los tres atributos son primos, y no hay ninguno no primo.
¿Está en 3FN? La 3FN exige que en toda dependencia no trivial X → A, o X sea superclave o A sea primo. Repasemos:
h1:X = {evento_id, rol}es superclave. Cumple.h2:X = {ponente_id}no es superclave ({ponente_id}⁺ = {ponente_id, rol}, faltan atributos). PeroA = rolsí es primo. Cumple por la segunda vía.
Sí está en 3FN.
¿Está en FNBC? La FNBC solo admite la primera vía:
h2:ponente_idno es superclave. Incumple.
No está en FNBC.
¿Y qué daño hace en la práctica? Míralo en los datos: el hecho "el ponente 7 es moderador" está escrito dos veces (filas 210 y 211), y el hecho "el ponente 9 es tallerista", otras dos. Si Clara Ferrán renegocia su contrato y pasa a tallerista, hay que actualizar todas sus filas o la base de datos dirá que es moderadora en unos eventos y tallerista en otros, contradiciendo N2. Es una anomalía de actualización clásica, dentro de una tabla que está en 3FN. Eso es exactamente lo que la FNBC viene a cazar.
Además, la anomalía de inserción: no se puede registrar que un ponente nuevo tiene el rol de tallerista hasta que se le asigne a algún evento.
Corrección: la regla es siempre la misma. Se saca la dependencia infractora a su propia tabla, con el determinante como clave primaria.
CREATE TABLE ponente_rol (
ponente_id INTEGER NOT NULL,
rol VARCHAR(25) NOT NULL,
CONSTRAINT pk_ponente_rol PRIMARY KEY (ponente_id),
CONSTRAINT fk_ponente_rol FOREIGN KEY (ponente_id) REFERENCES ponentes (ponente_id)
);
CREATE TABLE asignaciones (
evento_id INTEGER NOT NULL,
ponente_id INTEGER NOT NULL,
CONSTRAINT pk_asignaciones PRIMARY KEY (evento_id, ponente_id),
CONSTRAINT fk_asig_evento FOREIGN KEY (evento_id) REFERENCES eventos (evento_id),
CONSTRAINT fk_asig_ponente FOREIGN KEY (ponente_id) REFERENCES ponentes (ponente_id)
);ponente_rol
| ponente_id | rol |
|---|---|
| 7 | moderador |
| 9 | tallerista |
| 12 | autor_invitado |
asignaciones
| evento_id | ponente_id |
|---|---|
| 210 | 7 |
| 210 | 9 |
| 211 | 7 |
| 211 | 12 |
| 212 | 9 |
Cada rol de ponente, escrito una vez. Las dos tablas están en FNBC.
- Cuando la descomposición a FNBC no conserva las dependencias
Y ahora la letra pequeña, que es lo que hace que la FNBC no sea siempre la mejor idea.
Vuelve a mirar la descomposición que acabamos de hacer y hazte esta pregunta: ¿dónde ha quedado la regla N1 ("en un evento, cada rol lo desempeña una sola persona")?
Formalmente, la dependencia h1: {evento_id, rol} → ponente_id. En la descomposición:
- En
ponente_rolno está: no hayevento_id. - En
asignacionesno está: no hayrol.
La dependencia ha desaparecido de las dos tablas. Y su desaparición tiene una consecuencia práctica muy concreta: ya no hay ninguna combinación de PRIMARY KEY, UNIQUE o CHECK que impida esto:
INSERT INTO asignaciones (evento_id, ponente_id) VALUES (213, 7); -- moderador
INSERT INTO asignaciones (evento_id, ponente_id) VALUES (213, 20); -- otro moderadorSi el ponente 20 también es moderador según ponente_rol, acabamos de meter dos moderadores en el mismo evento, violando N1, y el SGBD no ha protestado porque las claves primarias de ambas tablas se respetan.
Para comprobar N1 hay que hacer un JOIN de las dos tablas:
-- Detectar violaciones de N1: eventos con dos personas en el mismo rol
SELECT a.evento_id, r.rol, COUNT(*) AS personas
FROM asignaciones a
JOIN ponente_rol r ON r.ponente_id = a.ponente_id
GROUP BY a.evento_id, r.rol
HAVING COUNT(*) > 1;Y una consulta no es una restricción: hay que ejecutarla, y para entonces el dato malo ya está dentro.
Definición. Una descomposición conserva las dependencias si cada dependencia funcional del conjunto original se puede comprobar mirando una sola tabla de la descomposición, sin necesidad de reunirlas.
El resultado teórico es este, y conviene conocerlo:
- Siempre existe una descomposición en 3FN que es a la vez sin pérdida de información y conservadora de las dependencias.
- No siempre existe una descomposición en FNBC que conserve las dependencias.
Nuestro ejemplo es precisamente un caso de los segundos. Ante él hay que elegir:
| Opción | Qué se gana | Qué se pierde |
|---|---|---|
Quedarse en 3FN (tabla asignaciones original) |
N1 la garantiza la clave primaria {evento_id, rol} |
El rol de cada ponente se repite; anomalías de actualización |
| Descomponer a FNBC | Cada rol escrito una vez; sin anomalías | N1 deja de ser verificable dentro de una tabla; hay que garantizarla con un disparador o en la aplicación |
No hay una respuesta universal. La decisión depende de qué dependencia se incumple más a menudo en la práctica y de cuál es más grave. Lo que no es aceptable es descomponer a FNBC y no darse cuenta de que se ha perdido una regla de negocio: entonces la regla simplemente deja de cumplirse y nadie se entera hasta que alguien pregunta por qué hay dos moderadores.
Sobre cómo se verifican formalmente estas dos propiedades —conservación de dependencias y descomposición sin pérdida— y sobre la condición de Heath que garantiza la segunda, va buena parte de la lección 05-03.
(Un apunte para evitar confusiones: la tabla participaciones del esquema real de BiblioRed, con clave {evento_id, ponente_id, rol}, no tiene este problema, porque allí no rige N2: un ponente puede hacer de moderador en un evento y de tallerista en otro. Sin la dependencia ponente_id → rol, participaciones está en FNBC. El ejemplo de esta sección es una variante hipotética construida para ilustrar el caso.)
- Cuarta forma normal (4FN): dependencias multivaluadas
Hasta aquí, todo ha girado en torno a dependencias funcionales. Existe otro tipo de dependencia que las funcionales no capturan, y para la que hace falta una forma normal propia.
El problema, primero
BiblioRed organiza el evento 210, "Club de lectura: novela histórica". Ese evento tiene:
- Dos ponentes: el 7 y el 9.
- Tres materiales recomendados: 101, 102 y 103.
Y aquí está el dato clave: los ponentes y los materiales no tienen nada que ver entre sí. El ponente 7 no está asociado a un material concreto; los tres materiales son del evento, no de un ponente. Son dos listas independientes que cuelgan del mismo evento.
Ahora imagina que alguien las mete en una sola tabla:
evento_recursos — en FNBC pero NO en 4FN
| evento_id | ponente_id | material_id |
|---|---|---|
| 210 | 7 | 101 |
| 210 | 7 | 102 |
| 210 | 7 | 103 |
| 210 | 9 | 101 |
| 210 | 9 | 102 |
| 210 | 9 | 103 |
Seis filas para representar dos hechos y tres hechos. 2 × 3 = 6: la tabla es un producto cartesiano disfrazado. Con tres ponentes y ocho materiales serían 24 filas para once hechos.
Observa lo raro del asunto: no hay ninguna dependencia funcional problemática. La única clave candidata es la tabla entera, {evento_id, ponente_id, material_id}; todos los atributos son primos; no hay ningún determinante que no sea superclave. La tabla está en FNBC y aun así es un desastre:
-- Anomalía de inserción: añadir un cuarto material al evento 210
-- obliga a insertar UNA FILA POR PONENTE, o los datos quedan inconsistentes.
INSERT INTO evento_recursos VALUES (210, 7, 104);
-- Si olvidas esta segunda, la tabla dice que el material 104
-- va con el ponente 7 pero no con el 9, lo cual no significa nada.
INSERT INTO evento_recursos VALUES (210, 9, 104);-- Anomalía de borrado: dar de baja al ponente 9 obliga a borrar 3 filas.
DELETE FROM evento_recursos WHERE evento_id = 210 AND ponente_id = 9;La dependencia multivaluada
Definición. En una relación
R, hay una dependencia multivaluada deYrespecto deX, escritaX ↠ Y(con doble punta de flecha), si el conjunto de valores deYasociados a un valor deXdepende solo deXy es independiente de los demás atributos de la relación.
Se lee "X multidetermina Y". En nuestro caso:
"El evento multidetermina sus ponentes": la lista de ponentes de un evento es la que es, sin que importe qué materiales tenga. Y viceversa.
Una dependencia multivaluada siempre viene en pareja: si X ↠ Y en una relación con atributos X, Y, Z, entonces también X ↠ Z. Por eso se escriben juntas: evento_id ↠ ponente_id | material_id.
Nota la relación entre los dos tipos de dependencia: toda dependencia funcional es una dependencia multivaluada (si X → Y, el conjunto de valores de Y para cada X tiene exactamente un elemento y no depende de nada más). Lo contrario no: evento_id ↠ ponente_id no es funcional, porque un evento tiene varios ponentes.
Definición. Una relación está en cuarta forma normal si está en FNBC y, para toda dependencia multivaluada no trivial
X ↠ Y,Xes superclave.
En evento_recursos, evento_id ↠ ponente_id es no trivial y evento_id no es superclave (no determina la fila entera). Viola la 4FN.
Corrección: separar las dos listas independientes en dos tablas. Que es, exactamente, lo que el esquema del módulo 4 ya hace:
CREATE TABLE participaciones (
evento_id INTEGER NOT NULL,
ponente_id INTEGER NOT NULL,
rol VARCHAR(25) NOT NULL,
CONSTRAINT pk_participaciones PRIMARY KEY (evento_id, ponente_id, rol),
CONSTRAINT fk_part_evento FOREIGN KEY (evento_id) REFERENCES eventos (evento_id),
CONSTRAINT fk_part_ponente FOREIGN KEY (ponente_id) REFERENCES ponentes (ponente_id)
);
CREATE TABLE eventos_materiales (
evento_id INTEGER NOT NULL,
material_id INTEGER NOT NULL,
papel VARCHAR(15) NOT NULL DEFAULT 'recomendado',
CONSTRAINT pk_eventos_materiales PRIMARY KEY (evento_id, material_id),
CONSTRAINT fk_em_evento FOREIGN KEY (evento_id) REFERENCES eventos (evento_id),
CONSTRAINT fk_em_material FOREIGN KEY (material_id) REFERENCES materiales (material_id)
);participaciones (2 filas) + eventos_materiales (3 filas) = 5 filas en lugar de 6. Y, más importante que el ahorro: añadir un material es una fila, y dar de baja a un ponente es una fila.
Este es un buen momento para señalar algo alentador. Cuando en el módulo 4 decidimos, por comprensión del dominio, que los ponentes y los materiales de un evento eran dos relaciones N:M distintas y les dimos dos tablas, estábamos poniendo el esquema en cuarta forma normal sin haber oído hablar de ella. El diseño por comprensión del dominio y la normalización formal llegan casi siempre al mismo sitio; la normalización sirve para verificarlo y para los casos en que la intuición falla.
El aviso importante sobre la 4FN
La 4FN solo se viola cuando hay dos o más relaciones multivaluadas independientes en la misma tabla. Si las dos listas no fueran independientes —por ejemplo, si cada material estuviera asociado a un ponente concreto ("el material 101 lo trae el ponente 7")— entonces la tabla de tres columnas sería correcta y necesaria: representaría una relación ternaria genuina, de las que vimos en 04-02 y resolvimos con la regla 9 en 04-03.
La pregunta de diagnóstico es siempre la misma: ¿el conjunto de valores de esta columna para un evento cambia según el valor de la otra columna? Si la respuesta es no, son independientes y hay que separarlas.
- Quinta forma normal (5FN): dependencias de reunión
La quinta forma normal, también llamada forma normal de proyección-reunión, es el último escalón con contenido práctico, y hay que decir con honestidad que rara vez aparece en la vida real. Se explica aquí para que sepas que existe y para que reconozcas el caso si alguna vez te lo encuentras, no porque vayas a aplicarla el mes que viene.
La idea generaliza lo anterior. La 4FN habla de tablas que se pueden partir en dos sin perder información. La 5FN habla de tablas que no se pueden partir en dos, pero sí en tres o más.
Definición. Una relación está en quinta forma normal si toda dependencia de reunión que se cumple en ella está implicada por sus claves candidatas. Una dependencia de reunión existe cuando la relación es igual a la reunión (JOIN) de varias de sus proyecciones.
El ejemplo típico requiere una regla de negocio cíclica. Supongamos que BiblioRed adquiere fondo con esta norma:
N3: si una sucursal trabaja con una editorial, y esa editorial publica una colección, y esa colección está presente en esa sucursal, entonces esa sucursal compra esa colección de esa editorial.
Suena rebuscado, y lo es: por eso la 5FN es rara. Pero si esa regla se cumple, la tabla compras(sucursal, editorial, coleccion) es reconstruible exactamente a partir de tres proyecciones —(sucursal, editorial), (editorial, coleccion) y (sucursal, coleccion)— y guardarla entera es redundante.
Lo que hay que retener:
- Una violación de 5FN solo aparece cuando existe una regla cíclica de este tipo entre tres o más atributos.
- Detectarla exige un análisis del dominio muy fino y errar en el diagnóstico produce descomposiciones que inventan filas al reunir (el problema de la pérdida de información, que veremos en 05-03).
- Toda relación en 4FN cuya clave candidata sea la relación entera o cuyas dependencias sean todas funcionales suele estar ya en 5FN.
Recomendación práctica: no busques violaciones de 5FN de forma sistemática. Si una tabla de tres o más claves ajenas te resulta sospechosamente redundante y detectas una regla de negocio cíclica, entonces investígala. En caso contrario, no está ahí.
- La forma normal de dominio y clave (FNDC): el límite teórico
Ronald Fagin definió en 1981 la forma normal de dominio y clave:
Una relación está en FNDC si todas las restricciones que debe cumplir son consecuencia lógica únicamente de las restricciones de dominio (los valores permitidos en cada columna) y de las restricciones de clave (las claves candidatas).
Es una definición elegantísima y con una propiedad notable: una relación en FNDC no tiene ninguna anomalía de modificación de ningún tipo, ni las conocidas ni las que se pudieran descubrir en el futuro. Es el techo teórico de la normalización.
Su problema es doble: no existe ningún algoritmo general para llevar una relación a FNDC, y muchas relaciones no admiten ninguna descomposición que las lleve allí. Por ejemplo, la regla de BiblioRed "la suma de los pagos de una multa no puede superar su importe" no es reducible a dominios ni a claves, y por tanto ninguna tabla que la necesite estará jamás en FNDC. La solución práctica para esas reglas es la que ya conocemos de 04-04: un disparador o la lógica de la aplicación.
Menciónala si alguien te pregunta; no la persigas.
- Tabla resumen de todas las formas normales
| Forma | Qué prohíbe | Cómo se detecta | Cómo se corrige |
|---|---|---|---|
| 1FN | Valores no atómicos: listas en una celda, grupos de columnas repetidas, filas duplicadas | Buscar comas o separadores dentro de valores; columnas con sufijo numérico (tel_1, tel_2); tablas sin PK |
Sacar el atributo multivaluado a una tabla propia con la clave ajena + el valor como PK compuesta |
| 2FN | Dependencias parciales: un atributo no primo depende de parte de la clave compuesta | Solo posible con clave compuesta. Para cada parte de la clave, calcular su cierre: si contiene atributos no primos, hay dependencia parcial | Sacar cada dependencia parcial a una tabla nueva con esa parte de la clave como PK |
| 3FN | Dependencias transitivas: un atributo no primo depende de otro no primo | Para cada dependencia no trivial X → A: ¿X es superclave? ¿A es primo? Si las dos respuestas son "no", se viola |
Sacar la dependencia a una tabla nueva con X como PK; dejar X como clave ajena |
| FNBC | Cualquier determinante que no sea superclave | Para cada dependencia no trivial X → A: ¿X es superclave? Si no, se viola |
Igual que 3FN. Atención: puede no conservar las dependencias |
| 4FN | Dependencias multivaluadas independientes en la misma tabla | Tabla de 3+ atributos donde el número de filas es el producto de dos listas independientes | Separar en una tabla por cada lista independiente |
| 5FN | Dependencias de reunión no implicadas por las claves | Regla de negocio cíclica entre 3+ atributos; la tabla es reconstruible reuniendo 3+ proyecciones | Descomponer en las proyecciones correspondientes |
| FNDC | Toda restricción que no sea de dominio o de clave | No hay algoritmo general | No hay método general; a menudo inalcanzable |
Y una tabla complementaria que ayuda a decidir por dónde empezar a buscar:
| Si la tabla… | Entonces… |
|---|---|
| Tiene clave de un solo atributo | Está automáticamente en 2FN. Empieza a comprobar por la 3FN |
| Tiene solo dos atributos | Está automáticamente en FNBC |
| No tiene atributos no primos (todos son parte de alguna clave) | Está automáticamente en 3FN; comprueba FNBC y 4FN |
| Tiene una sola clave candidata y está en 3FN | Está automáticamente en FNBC |
La última fila es especialmente útil: si la tabla tiene una única clave candidata, 3FN y FNBC coinciden. Como la inmensa mayoría de las tablas con clave subrogada tienen una sola clave candidata, en la práctica llegar a 3FN suele significar haber llegado a FNBC.
- El criterio real de la industria
Con siete formas normales sobre la mesa, la pregunta obligada es: ¿hasta dónde hay que llegar?
La respuesta que se aplica en prácticamente todos los sistemas transaccionales serios es esta:
Hasta 3FN o FNBC, siempre. Más allá, solo si el caso concreto lo pide.
Y las razones son sólidas, no una excusa para trabajar menos:
Hasta 3FN/FNBC el beneficio es enorme y el coste es bajo. Eliminar dependencias parciales y transitivas quita la práctica totalidad de las anomalías que se producen en un sistema real, y las tablas resultantes son las que cualquier profesional del sector espera encontrar. Además, las descomposiciones son fáciles de razonar y de explicar.
Más allá de FNBC, el rendimiento decreciente es brusco. Las violaciones de 4FN son poco frecuentes y, cuando aparecen, suelen ser tan evidentes (una tabla con seis filas donde deberían caber cinco hechos) que se detectan por sentido común. Las de 5FN son rarísimas, difíciles de diagnosticar y fáciles de diagnosticar mal.
Un esquema en 3FN es un esquema que otros entienden. Esto tiene más peso del que parece. Si dentro de tres años alguien tiene que mantener la base de datos de BiblioRed, encontrará tablas con la forma que espera. Un esquema descompuesto hasta la quinta forma normal, con tablas que existen por razones que solo se entienden con el papel de las dependencias delante, es un esquema que la siguiente persona romperá sin querer.
El criterio operativo, entonces:
| Nivel | Cuándo | Esfuerzo |
|---|---|---|
| 1FN | Siempre, sin excepción. Una violación de 1FN rompe las consultas | Obligatorio |
| 2FN | Siempre. Revisa toda tabla con clave compuesta | Obligatorio |
| 3FN | Siempre. Es el objetivo por defecto | Obligatorio |
| FNBC | Cuando aparezcan claves candidatas solapadas. Valorar si merece la pena perder la conservación de dependencias | Recomendado, con criterio |
| 4FN | Cuando detectes una tabla que multiplica filas de dos listas independientes | Solo si aparece |
| 5FN | Cuando exista una regla de negocio cíclica documentada | Casi nunca |
| FNDC | Nunca como objetivo de diseño | Interés teórico |
Y el corolario, que anticipa la última lección del módulo: normaliza hasta 3FN/FNBC como línea de base, y solo entonces, con datos de rendimiento en la mano, considera retroceder deliberadamente. Retroceder desde un diseño normalizado es una decisión informada; no haber normalizado nunca es simplemente no haber hecho el trabajo.
Errores Comunes y Consejos
Creer que se puede determinar la forma normal mirando solo la tabla. No se puede. Sin conocer las dependencias funcionales —es decir, sin conocer las reglas del negocio— cualquier respuesta es una suposición. Si alguien te enseña un CREATE TABLE y te pregunta en qué forma normal está, la respuesta correcta empieza por "depende de qué reglas rigen estas columnas".
Saltarse niveles al comprobar. Es tentador ir directo a la 3FN porque parece la más importante. Pero si la tabla tiene una lista separada por comas, ni siquiera está en 1FN, y hablar de dependencias transitivas sobre ella no tiene sentido. Comprueba de abajo arriba y corrige antes de subir.
Buscar dependencias parciales en tablas con clave simple. Es tiempo perdido: no pueden existir. Si la clave es un solo atributo, la tabla está en 2FN por construcción. Ese atajo ahorra la mitad del trabajo en un esquema con claves subrogadas.
Confundir "muchos valores" con "no atómico". Que un socio tenga tres teléfonos no viola la 1FN; lo que la viola es meterlos en la misma celda o en tres columnas. La tabla telefonos_socio con tres filas es perfectamente 1FN: cada celda tiene un valor.
Aplicar 1FN a rajatabla contra los tipos compuestos modernos. Una columna JSONB con datos que siempre se leen enteros, sin reglas de negocio sobre sus campos, es aceptable. La discusión útil no es "¿esto es atómico?" sino "¿voy a necesitar filtrar, agrupar o restringir por una parte de esto?".
Descomponer a FNBC sin comprobar qué dependencias se pierden. Es el error más caro de esta lección, porque el resultado parece mejor: menos redundancia, tablas más limpias. Y sin embargo una regla de negocio ha dejado de estar garantizada. Antes de descomponer a FNBC, haz la lista de dependencias originales y comprueba una a una en qué tabla queda cada una. Si alguna no queda en ninguna, decide conscientemente entre quedarte en 3FN o añadir un disparador.
Perseguir la 5FN. Si te encuentras razonando sobre dependencias de reunión en un proyecto normal, casi seguro que has diagnosticado mal algo más abajo. Vuelve a comprobar la 3FN.
Consejo de método: cuando revises un esquema existente, hazlo tabla por tabla y escribe para cada una tres líneas: sus dependencias, sus claves candidatas y su forma normal más alta. Es un documento de media hora que sirve durante años y que convierte las discusiones de diseño en discusiones con datos.
Ejercicios
Ejercicio 1: Diagnosticar la forma normal más alta
Para cada una de estas tres tablas de BiblioRed, determina la forma normal más alta que cumple y, si no llega a 3FN, indica qué dependencia lo impide y qué anomalía produce.
a) reservas_v0(socio_id, material_id, fecha_reserva, socio_email, estado)
Reglas: un socio solo puede tener una reserva viva por material; el correo identifica al socio.
b) multas_v0(multa_id, socio_id, motivo, tarifa_dia, dias_retraso, importe)
Reglas: cada motivo tiene una tarifa diaria fija establecida por ordenanza (retraso = 0,10 €/día, deterioro = 2,00 €/día); el importe es la tarifa por los días.
c) subtitulos_dvd(material_id, idioma)
Reglas: un DVD puede tener subtítulos en varios idiomas; no hay más reglas.
Ejercicio 2: 3FN sí, FNBC no
BiblioRed asigna a cada socio un bibliotecario de referencia con esta tabla:
referencias(socio_id, especialidad, bibliotecario_id)
Reglas de negocio:
- P1: para cada socio y cada especialidad (infantil, literatura, técnica) hay un único bibliotecario de referencia.
- P2: cada bibliotecario está especializado en una única especialidad.
Se pide:
- a) Escribir las dependencias funcionales.
- b) Encontrar todas las claves candidatas calculando cierres.
- c) Demostrar que la tabla está en 3FN pero no en FNBC.
- d) Proponer la descomposición a FNBC y decir qué dependencia se pierde.
Ejercicio 3: ¿4FN o relación ternaria?
Para cada uno de estos dos casos, di si la tabla de tres columnas viola la 4FN (y hay que separarla en dos) o si representa una relación ternaria legítima (y hay que dejarla como está). Justifica con la pregunta de diagnóstico de la sección 9.
a) evento_idiomas_accesibilidad(evento_id, idioma, servicio_accesibilidad)
Un evento se ofrece en varios idiomas (catalán, castellano) y dispone de varios servicios de accesibilidad (bucle magnético, intérprete de signos, subtitulado en directo). Los servicios están disponibles para todo el evento, independientemente del idioma.
b) participaciones(evento_id, ponente_id, rol)
En el esquema real de BiblioRed: un evento tiene varios ponentes, y cada ponente desempeña uno o varios roles en ese evento concreto. Que el ponente 7 sea moderador en el evento 210 no dice nada sobre qué hace en el 211.
Soluciones
Solución 1
a) reservas_v0 está en 1FN, no llega a 2FN.
Dependencias:
La clave candidata es {socio_id, material_id} (la regla "una reserva viva por socio y material"). Atributos primos: socio_id, material_id.
r2 es una dependencia parcial: socio_id es media clave y ya determina socio_email, que es no primo. Por tanto la tabla no está en 2FN, y la forma normal más alta que cumple es la 1FN. Este es un buen recordatorio de por qué se comprueba de abajo arriba: quien vaya directo a buscar dependencias transitivas se encontrará con que la pregunta ni siquiera procede.
La anomalía es de actualización: si Marta Alsina corrige su correo, hay que tocar todas sus reservas, y basta olvidar una para tener dos correos para la misma persona —exactamente el defecto 5 del diagnóstico de 01-01. La corrección es quitar socio_email de aquí: ya vive en socios, y desde reservas se llega por la clave ajena socio_id.
b) multas_v0 está en 2FN, no en 3FN.
Dependencias:
m1: multa_id → {socio_id, motivo, dias_retraso}
m2: motivo → tarifa_dia
m3: {tarifa_dia, dias_retraso} → importeLa clave candidata es {multa_id}, de un solo atributo, así que la 2FN está garantizada.
m2 viola la 3FN: motivo no es superclave ({motivo}⁺ = {motivo, tarifa_dia}) y tarifa_dia no es primo. Es una dependencia transitiva multa_id → motivo → tarifa_dia. La anomalía: si la ordenanza sube la tarifa de retraso a 0,15 €/día, hay que actualizar todas las multas de retraso del histórico —y eso, además de costoso, es incorrecto, porque las multas ya emitidas se calcularon con la tarifa antigua.
La corrección es una tabla tarifas_multa(motivo, tarifa_dia) con motivo como clave primaria y una clave ajena desde multas.
m3 también es una dependencia transitiva (importe es un valor derivado). Aquí la respuesta correcta no es evidente y es un anticipo perfecto de la lección siguiente: el importe debe quedarse en multas porque es un dato histórico que tiene que permanecer congelado con el valor que tuvo el día de la emisión, aunque la tarifa cambie después. Es una desnormalización deliberada y justificada, no un descuido.
c) subtitulos_dvd está en FNBC (y en 4FN, y en 5FN).
La única dependencia no trivial posible sería entre material_id e idioma, y no existe en ninguna dirección: un DVD tiene varios idiomas y un idioma está en varios DVD. La clave candidata es la tabla entera, {material_id, idioma}; ambos atributos son primos; no hay ningún determinante que no sea superclave. Toda tabla de exactamente dos atributos cuya clave es la pareja está en FNBC por construcción. Es un ejemplo de que las tablas de asociación pura son las más "normales" que existen.
Solución 2
a) Dependencias:
b) Claves candidatas.
{socio_id, especialidad}⁺: porp1entrabibliotecario_id. Ya están los tres. Superclave. ¿Mínima?{socio_id}⁺ = {socio_id}(ninguna dependencia arranca solo consocio_id);{especialidad}⁺ = {especialidad}. Ninguna mitad basta. Clave candidata 1.{socio_id, bibliotecario_id}⁺: porp2entraespecialidad. Los tres. Superclave. ¿Mínima?{bibliotecario_id}⁺ = {bibliotecario_id, especialidad}, no contienesocio_id. Y{socio_id}⁺ya vimos que no crece. Clave candidata 2.
Dos claves candidatas, compuestas y solapadas en socio_id. Atributos primos: los tres (socio_id en ambas, especialidad en la 1, bibliotecario_id en la 2). No hay atributos no primos.
c) Está en 3FN. La 3FN exige, para cada dependencia no trivial X → A, que X sea superclave o A sea primo:
p1:{socio_id, especialidad}es superclave. Cumple.p2:bibliotecario_idno es superclave, peroespecialidades primo (está en la clave candidata 1). Cumple por la segunda vía.
No está en FNBC, porque p2 tiene un determinante, bibliotecario_id, que no es superclave, y la FNBC no admite la excusa de que el determinado sea primo.
El daño concreto: la especialidad de cada bibliotecario está repetida en todas las filas de socios que lo tienen asignado. Si el bibliotecario 22 cambia de especialidad, hay que actualizar cientos de filas. Y no se puede registrar la especialidad de un bibliotecario recién contratado hasta que se le asigne algún socio.
d) Descomposición a FNBC: se saca la dependencia infractora.
CREATE TABLE bibliotecarios_especialidad (
bibliotecario_id INTEGER NOT NULL,
especialidad VARCHAR(20) NOT NULL,
CONSTRAINT pk_biblio_esp PRIMARY KEY (bibliotecario_id)
);
CREATE TABLE referencias (
socio_id INTEGER NOT NULL,
bibliotecario_id INTEGER NOT NULL,
CONSTRAINT pk_referencias PRIMARY KEY (socio_id, bibliotecario_id)
);Se pierde p1: {socio_id, especialidad} → bibliotecario_id. Ni bibliotecarios_especialidad ni referencias contienen a la vez socio_id y especialidad, así que la regla P1 ("un solo bibliotecario por socio y especialidad") ya no la puede garantizar ninguna restricción declarativa. Nada impide asignar a Marta Alsina dos bibliotecarios que resulten ser ambos de literatura.
La decisión es la que planteamos en la sección 8: quedarse en 3FN y garantizar P1 con la clave primaria {socio_id, especialidad}, aceptando la redundancia de la especialidad; o descomponer a FNBC y garantizar P1 con un disparador. Si los bibliotecarios cambian de especialidad muy raramente —lo habitual— la primera opción es más sensata.
Solución 3
a) Viola la 4FN. Aplicamos la pregunta de diagnóstico: ¿cambia el conjunto de servicios de accesibilidad de un evento según el idioma? El enunciado dice explícitamente que no: los servicios están disponibles para todo el evento. Son dos listas independientes que cuelgan del mismo evento_id, y hay dos dependencias multivaluadas:
Un evento en 2 idiomas con 3 servicios generaría 6 filas para 5 hechos, y añadir un cuarto servicio obligaría a insertar 2 filas. La corrección es separar en eventos_idiomas(evento_id, idioma) y eventos_accesibilidad(evento_id, servicio).
b) No viola la 4FN: es una relación ternaria legítima. Misma pregunta: ¿cambia el conjunto de roles según el evento? Sí, rotundamente: el enunciado dice que el rol del ponente 7 en el evento 210 no dice nada sobre lo que hace en el 211. El rol es un atributo de la participación concreta, no una propiedad independiente ni del evento ni del ponente.
Formalmente, no existe evento_id ↠ rol independiente de ponente_id, así que no hay dependencia multivaluada que violar. Separar esta tabla sería un error grave: perdería la información de quién hace qué, que es precisamente lo que se quiere guardar. Es exactamente el caso que resolvimos con la regla 9 de transformación en 04-03.
La diferencia entre los dos casos es la lección que hay que llevarse: la estructura de la tabla no dice si viola la 4FN; lo dice la regla de negocio. Dos tablas con tres columnas cada una, una hay que partirla y la otra no.
Conclusión
Ya tienes el catálogo completo, y con él la capacidad de emitir un juicio verificable sobre cualquier tabla.
La 1FN exige valores atómicos: ni listas dentro de una celda, ni columnas numeradas, ni filas duplicadas. Es la que convierte los anti-patrones que denunciamos en 04-01 en violaciones con nombre, y su corrección es la regla 3 de transformación que ya aplicamos en 04-03. Aprendimos también que "atómico" no es una propiedad del dato sino de su uso: una fecha lo es, un JSONB que solo se lee entero lo es en la práctica, y una dirección lo es hasta el día en que hay que agrupar por código postal.
La 2FN elimina las dependencias parciales, que solo pueden existir en tablas con clave compuesta. La 3FN elimina las transitivas, en las que un atributo no primo depende de otro no primo. Las dos son obligatorias y las dos se corrigen igual: se saca la dependencia infractora a una tabla propia, con el determinante como clave primaria y una clave ajena en la original.
La FNBC cierra la rendija que deja la 3FN con una definición de una sola línea —todo determinante es superclave— y solo se distingue de ella cuando hay claves candidatas compuestas y solapadas. Su letra pequeña es fundamental: la descomposición a FNBC puede perder la conservación de dependencias, y eso significa que una regla de negocio deja de poder garantizarse dentro de una sola tabla. Se decide con criterio, no automáticamente.
La 4FN ataca un problema distinto: dos listas independientes metidas en la misma tabla, que se multiplican entre sí sin significar nada. Su remedio —una tabla por lista— es el que el módulo 4 ya había aplicado por intuición a participaciones y eventos_materiales. La 5FN y la FNDC existen, son coherentes, y en un sistema como BiblioRed no se aplican; conviene saber que están ahí y no perseguirlas.
Y por encima de todo el catálogo, el criterio de la industria: hasta 3FN/FNBC siempre, más allá solo si el caso lo pide. No por pereza, sino porque el rendimiento decreciente es brusco y porque un esquema en 3FN es un esquema que la siguiente persona entenderá.
Lo que no hemos hecho todavía es aplicar nada de esto de principio a fin sobre una tabla real. Hemos visto seis ejemplos mínimos, cada uno construido para ilustrar una forma normal aislada. La realidad no llega así: llega como la hoja de cálculo de BiblioRed, con trece columnas, once dependencias, valores no atómicos, dependencias parciales y transitivas a la vez, ochenta y cuatro mil filas de datos históricos que hay que migrar y limpiar, y un sistema en producción que no se puede parar.
En la lección 05-03, Proceso de Normalización, hacemos exactamente eso. Un procedimiento de seis pasos, aplicado de principio a fin sobre prestamos_hoja: de una tabla plana sin normalizar hasta 3FN, mostrando en cada paso los datos antes y después y el SQL que descompone y migra. Y con ello, las dos propiedades que toda descomposición debe cumplir —sin pérdida de información y conservación de las dependencias—, incluido el contraejemplo de una descomposición mal hecha que inventa filas al reunir. Terminaremos sometiendo a examen el esquema ampliado del módulo 4, tabla por tabla, para ver si aguanta.
Fundamentos de Bases de Datos
Módulo 1: Introducción a las Bases de Datos
- Conceptos Básicos de Bases de Datos
- Tipos de Bases de Datos
- Historia y Evolución de las Bases de Datos
- Sistemas Gestores de Bases de Datos y Arquitectura
Módulo 2: Bases de Datos Relacionales
- Modelo Relacional
- Lenguaje SQL
- Operaciones Básicas en SQL
- Consultas Multitabla: JOIN y Subconsultas
- Agregación y Agrupación de Datos
- Integridad Referencial
Módulo 3: Bases de Datos No Relacionales
- Introducción a NoSQL
- Tipos de Bases de Datos NoSQL
- Modelado de Datos en NoSQL
- Comparación entre Bases de Datos Relacionales y No Relacionales
Módulo 4: Diseño de Esquemas
- Principios de Diseño de Esquemas
- Diagramas Entidad-Relación (ER)
- Transformación de Diagramas ER a Esquemas Relacionales
- Tipos de Datos y Restricciones
Módulo 5: Normalización
Módulo 6: Transacciones, Rendimiento y Seguridad
- Transacciones y Propiedades ACID
- Concurrencia y Niveles de Aislamiento
- Índices y Optimización de Consultas
- Seguridad, Permisos y Copias de Seguridad
Módulo 7: Ejercicios Prácticos
- Ejercicios de SQL
- Ejercicios de Diseño de Esquemas
- Ejercicios de Normalización
- Ejercicios de Consultas Avanzadas y Transacciones
Módulo 8: Casos de Estudio
- Caso de Estudio: Base de Datos Relacional
- Caso de Estudio: Base de Datos No Relacional
- Caso de Estudio: Persistencia Políglota
