La Tercera Forma Normal explicada paso a paso con ejemplos

D
DanisCh
(Actualizado: ) • 13 min de lectura
La Tercera Forma Normal explicada paso a paso con ejemplos
Conceptos de Base de Datos

Has aprendido que la Primera Forma Normal elimina los valores múltiples en celdas y las columnas repetidas. La Segunda Forma Normal elimina los datos que dependen solo de una parte de la clave primaria compuesta. Con esas dos reglas el diseño ya es mucho más limpio. Pero todavía puede quedar un tipo de redundancia oculta que ninguna de las dos detecta.

Imagina una tabla donde cada columna depende correctamente de la clave primaria completa. A simple vista parece perfecta. Sin embargo, si una de esas columnas determina el valor de otra columna sin pasar por la clave, los datos se duplican, las actualizaciones se vuelven peligrosas y la integridad sigue en riesgo.

Ese problema lo resuelve la Tercera Forma Normal (3FN).

El concepto central: dependencia transitiva

Antes de ver ejemplos, necesitas entender qué es una dependencia transitiva. Es el tipo de dependencia que viola la 3FN.

Una dependencia transitiva ocurre cuando:

  • La columna A determina la columna B: A → B
  • La columna B determina la columna C: B → C
  • Por lo tanto, A determina C de forma indirecta, a través de B: A → B → C
  • Y B no es una clave candidata de la tabla.

Cuando A es la clave primaria, decimos que C depende transitivamente de la clave a través de B. Eso es una violación de la 3FN.

Un ejemplo de la vida cotidiana para que quede claro:

  • El ID del empleado determina su código postal: id_empleado → codigo_postal
  • El código postal determina la ciudad: codigo_postal → ciudad
  • Por tanto: id_empleado → codigo_postal → ciudad

La ciudad depende transitivamente del empleado a través del código postal. La ciudad no describe directamente al empleado: describe al código postal. Eso es exactamente lo que la 3FN no permite.

Definición formal de la Tercera Forma Normal

Una tabla está en Tercera Forma Normal cuando cumple estas condiciones:

  • Ya está en Segunda Forma Normal.
  • Ningún atributo que no sea parte de la clave primaria depende de otro atributo que tampoco sea parte de la clave primaria.

Dicho de forma más directa: todos los atributos no clave deben depender directamente de la clave primaria, y solo de la clave primaria. No de otros atributos no clave.

Una regla mnemotécnica que se usa mucho para recordarlo es la frase de Bill Kent: "Every non-key attribute must depend on the key, the whole key, and nothing but the key". En español: cada atributo no clave debe depender de la clave, de toda la clave, y de nada más que la clave.

  • "De la clave" → Primera Forma Normal: la clave existe y los valores son atómicos.
  • "De toda la clave" → Segunda Forma Normal: no hay dependencias parciales.
  • "De nada más que la clave" → Tercera Forma Normal: no hay dependencias transitivas.

Primer ejemplo: empleados con datos de ubicación

Veamos el ejemplo más clásico. Una tabla de empleados que incluye su ubicación:

Tabla empleados (viola la 3FN):

id_empleado (PK)nombredepartamentosalariocodigo_postalciudadprovincia
E001Laura MartínDesarrollo4200028001MadridMadrid
E002Carlos RuizDiseño3800008001BarcelonaCataluña
E003Sofía MoraDesarrollo4500028001MadridMadrid
E004Pedro DíazMarketing3600008001BarcelonaCataluña

Esta tabla ya está en 1FN (valores atómicos, clave primaria definida) y en 2FN (la clave es una sola columna, no hay dependencias parciales posibles). Pero hay un problema sutil.

Analicemos las dependencias:

  • id_empleado → nombre: el nombre depende directamente del empleado. ✅
  • id_empleado → departamento: el departamento depende directamente del empleado. ✅
  • id_empleado → salario: el salario depende directamente del empleado. ✅
  • id_empleado → codigo_postal: el código postal depende directamente del empleado. ✅
  • codigo_postal → ciudad: la ciudad depende del código postal, no del empleado. ⚠️
  • codigo_postal → provincia: la provincia depende del código postal, no del empleado. ⚠️

La ciudad y la provincia dependen transitivamente del empleado a través del código postal:

id_empleado → codigo_postal → ciudad
id_empleado → codigo_postal → provincia

Ciudad y provincia no describen al empleado directamente. Describen al código postal. Eso viola la 3FN.

Los problemas concretos

  • Redundancia: Madrid y Cataluña aparecen repetidas en varias filas. Con miles de empleados, esa redundancia se multiplica.
  • Anomalía de actualización: si el código postal 28001 cambia su nombre de ciudad (algo posible en casos de reorganización administrativa), hay que actualizar todas las filas de empleados con ese código postal. Si se olvida alguna, la base de datos tiene datos contradictorios.
  • Anomalía de inserción: no puedes registrar que el código postal 41001 pertenece a Sevilla sin tener un empleado con ese código.
  • Anomalía de eliminación: si eliminas todos los empleados con código postal 08001, pierdes la información de que ese código corresponde a Barcelona.

Cómo corregirlo: extraer la dependencia transitiva a su propia tabla

La solución es crear una tabla separada para los datos que dependen del código postal, y dejar en la tabla de empleados solo lo que depende directamente del empleado.

Tabla codigos_postales (nueva):

codigo_postal (PK)ciudadprovincia
28001MadridMadrid
08001BarcelonaCataluña

Tabla empleados (corregida):

id_empleado (PK)nombredepartamentosalariocodigo_postal (FK)
E001Laura MartínDesarrollo4200028001
E002Carlos RuizDiseño3800008001
E003Sofía MoraDesarrollo4500028001
E004Pedro DíazMarketing3600008001

Ahora ciudad y provincia se guardan una sola vez. Si el nombre de una ciudad cambia, se actualiza en un solo lugar. Puedes registrar códigos postales sin necesidad de tener empleados. Y si un empleado se borra, no se pierde la información geográfica.

Para obtener todos los datos juntos cuando se necesita, un JOIN es suficiente:

SELECT
  e.nombre,
  e.departamento,
  e.salario,
  cp.ciudad,
  cp.provincia
FROM empleados e
JOIN codigos_postales cp ON cp.codigo_postal = e.codigo_postal;

Segundo ejemplo: productos con datos del proveedor

Un catálogo de productos que incluye información del proveedor:

Tabla productos (viola la 3FN):

id_producto (PK)nombreprecioid_proveedornombre_proveedorpais_proveedorcontacto_proveedor
P001Laptop Pro1299.99V10TechSupply S.A.Españaventas@techsupply.com
P002Mouse45.50V10TechSupply S.A.Españaventas@techsupply.com
P003Teclado89.99V10TechSupply S.A.Españaventas@techsupply.com
P004Monitor 27"349.99V11ScreenWorldAlemaniainfo@screenworld.de
P005Hub USB34.99V11ScreenWorldAlemaniainfo@screenworld.de

Esta tabla está en 1FN y en 2FN (clave primaria simple). Pero analicemos las dependencias:

  • id_producto → nombre ✅
  • id_producto → precio ✅
  • id_producto → id_proveedor ✅ El producto tiene un proveedor.
  • id_proveedor → nombre_proveedor ⚠️ El nombre del proveedor depende del proveedor, no del producto.
  • id_proveedor → pais_proveedor ⚠️ Ídem.
  • id_proveedor → contacto_proveedor ⚠️ Ídem.

La cadena transitiva es clara:

id_producto → id_proveedor → nombre_proveedor
id_producto → id_proveedor → pais_proveedor
id_producto → id_proveedor → contacto_proveedor

TechSupply S.A. aparece tres veces, con España y su email repetidos en cada fila. Si cambian de email, hay que actualizar tres filas. Si añaden un producto nuevo de ese proveedor, los datos se vuelven a repetir.

Solución aplicando la 3FN

Tabla proveedores (nueva):

id_proveedor (PK)nombrepaiscontacto
V10TechSupply S.A.Españaventas@techsupply.com
V11ScreenWorldAlemaniainfo@screenworld.de

Tabla productos (corregida):

id_producto (PK)nombreprecioid_proveedor (FK)
P001Laptop Pro1299.99V10
P002Mouse45.50V10
P003Teclado89.99V10
P004Monitor 27"349.99V11
P005Hub USB34.99V11

Los datos de TechSupply ahora aparecen una sola vez. Si cambian su email, una actualización en un solo registro resuelve todo.

Tercer ejemplo: pedidos con datos del empleado que los gestiona

Un ejemplo con una cadena transitiva un poco más larga para que veas que el mismo principio se aplica sin importar cuántos escalones tenga:

Tabla pedidos (viola la 3FN):

id_pedido (PK)fechaid_empleadonombre_empleadoid_departamentonombre_departamentoedificio
10012024-06-01E001Laura MartínD01VentasEdificio A
10022024-06-03E001Laura MartínD01VentasEdificio A
10032024-06-05E002Carlos RuizD01VentasEdificio A
10042024-06-07E003Sofía MoraD02DesarrolloEdificio B

Las dependencias transitivas aquí forman una cadena de dos eslabones:

id_pedido → id_empleado → nombre_empleado
id_pedido → id_empleado → id_departamento → nombre_departamento
id_pedido → id_empleado → id_departamento → edificio

Hay que crear una tabla para cada eslabón de la cadena:

Tabla departamentos:

id_departamento (PK)nombreedificio
D01VentasEdificio A
D02DesarrolloEdificio B

Tabla empleados:

id_empleado (PK)nombreid_departamento (FK)
E001Laura MartínD01
E002Carlos RuizD01
E003Sofía MoraD02

Tabla pedidos (corregida):

id_pedido (PK)fechaid_empleado (FK)
10012024-06-01E001
10022024-06-03E001
10032024-06-05E002
10042024-06-07E003

Para consultar todos los datos juntos:

SELECT
  p.id_pedido,
  p.fecha,
  e.nombre        AS empleado,
  d.nombre        AS departamento,
  d.edificio
FROM pedidos p
JOIN empleados    e ON e.id_empleado    = p.id_empleado
JOIN departamentos d ON d.id_departamento = e.id_departamento
ORDER BY p.fecha;

Cuarto ejemplo: cursos con datos del instructor

Un caso de plataforma educativa para consolidar el concepto:

Tabla cursos (viola la 3FN):

id_curso (PK)tituloduracion_horasid_instructornombre_instructorespecialidademail_instructor
C01Python Básico20I01Marta SanzProgramaciónmarta@escuela.com
C02Python Avanzado30I01Marta SanzProgramaciónmarta@escuela.com
C03Bases de Datos25I02Jorge RíosDatosjorge@escuela.com
C04SQL Avanzado15I02Jorge RíosDatosjorge@escuela.com

Dependencias transitivas:

id_curso → id_instructor → nombre_instructor
id_curso → id_instructor → especialidad
id_curso → id_instructor → email_instructor

Marta Sanz y sus datos aparecen repetidos en dos filas. Si cambia su email, hay que actualizarlo en múltiples lugares. Si se elimina su último curso, desaparecen todos sus datos.

Solución

Tabla instructores:

id_instructor (PK)nombreespecialidademail
I01Marta SanzProgramaciónmarta@escuela.com
I02Jorge RíosDatosjorge@escuela.com

Tabla cursos (corregida):

id_curso (PK)tituloduracion_horasid_instructor (FK)
C01Python Básico20I01
C02Python Avanzado30I01
C03Bases de Datos25I02
C04SQL Avanzado15I02

Cómo detectar una dependencia transitiva: el método de las preguntas

Para cualquier columna de tu tabla que no forme parte de la clave primaria, hazte estas dos preguntas en orden:

Pregunta 1: ¿Esta columna describe directamente a la entidad principal de la tabla?

  • En una tabla de empleados: ¿el nombre del departamento describe al empleado? No, describe al departamento. ❌ Sospechosa.
  • En una tabla de empleados: ¿el salario describe al empleado? Sí, el salario es una propiedad del empleado. ✅ Bien ubicada.

Pregunta 2: Si la respuesta anterior es "no", ¿hay otra columna no clave en la tabla que determine el valor de esta?

  • ¿El código postal determina la ciudad? Sí. → Dependencia transitiva confirmada. La ciudad debe ir en otra tabla con el código postal como clave.
  • ¿El ID del proveedor determina el nombre del proveedor? Sí. → Dependencia transitiva confirmada. El nombre del proveedor debe ir en la tabla de proveedores.

Diferencia clave entre 2FN y 3FN

Es común confundir ambas formas normales. La diferencia es la siguiente:

AspectoSegunda Forma NormalTercera Forma Normal
Qué eliminaDependencias parciales de la claveDependencias transitivas entre atributos no clave
RequiereClave primaria compuestaCualquier tipo de clave primaria
El problema esUna columna depende de PARTE de la claveUna columna depende de OTRA COLUMNA no clave
La solución esSeparar en tablas según la parte de clave que determina cada grupoSeparar en tablas cada entidad que aparece como intermediaria

Una forma de recordarlo:

  • 2FN pregunta: ¿Depende de toda la clave o solo de parte de ella?
  • 3FN pregunta: ¿Depende de la clave directamente o pasa por otro atributo no clave?

El esquema completo en SQL aplicando 1FN, 2FN y 3FN

Para cerrar, veamos cómo quedaría un sistema de gestión de pedidos que aplica correctamente las tres formas normales:

-- 3FN: cada entidad en su propia tabla
-- Los datos de una entidad solo viven en un lugar

CREATE TABLE provincias (
  id       INT          AUTO_INCREMENT PRIMARY KEY,
  nombre   VARCHAR(100) NOT NULL
);

CREATE TABLE ciudades (
  id           INT          AUTO_INCREMENT PRIMARY KEY,
  nombre       VARCHAR(100) NOT NULL,
  id_provincia INT          NOT NULL,
  FOREIGN KEY (id_provincia) REFERENCES provincias(id)
);

CREATE TABLE codigos_postales (
  codigo       CHAR(5)  PRIMARY KEY,
  id_ciudad    INT      NOT NULL,
  FOREIGN KEY (id_ciudad) REFERENCES ciudades(id)
);

CREATE TABLE clientes (
  id             INT          AUTO_INCREMENT PRIMARY KEY,
  nombre         VARCHAR(100) NOT NULL,
  email          VARCHAR(100) UNIQUE NOT NULL,
  codigo_postal  CHAR(5),
  FOREIGN KEY (codigo_postal) REFERENCES codigos_postales(codigo)
);

CREATE TABLE categorias (
  id     INT          AUTO_INCREMENT PRIMARY KEY,
  nombre VARCHAR(50)  NOT NULL
);

CREATE TABLE proveedores (
  id       INT          AUTO_INCREMENT PRIMARY KEY,
  nombre   VARCHAR(100) NOT NULL,
  pais     VARCHAR(50),
  contacto VARCHAR(100)
);

CREATE TABLE productos (
  id           INT            AUTO_INCREMENT PRIMARY KEY,
  nombre       VARCHAR(100)   NOT NULL,
  precio       DECIMAL(10,2)  NOT NULL,
  stock        INT            DEFAULT 0,
  id_categoria INT,
  id_proveedor INT,
  FOREIGN KEY (id_categoria) REFERENCES categorias(id),
  FOREIGN KEY (id_proveedor) REFERENCES proveedores(id)
);

CREATE TABLE pedidos (
  id         INT  AUTO_INCREMENT PRIMARY KEY,
  id_cliente INT  NOT NULL,
  fecha      DATE NOT NULL,
  estado     VARCHAR(20) DEFAULT 'pendiente',
  FOREIGN KEY (id_cliente) REFERENCES clientes(id)
);

-- Tabla de unión: solo lo que depende de (id_pedido, id_producto) juntos
CREATE TABLE detalle_pedidos (
  id_pedido    INT            NOT NULL,
  id_producto  INT            NOT NULL,
  cantidad     INT            NOT NULL,
  precio_unit  DECIMAL(10,2)  NOT NULL,
  PRIMARY KEY (id_pedido, id_producto),
  FOREIGN KEY (id_pedido)   REFERENCES pedidos(id),
  FOREIGN KEY (id_producto) REFERENCES productos(id)
);

Cada tabla tiene exactamente los datos que le corresponden. No hay redundancias, no hay dependencias parciales, no hay dependencias transitivas. El esquema está en 3FN.

Lista de verificación para la 3FN

Antes de dar una tabla por válida respecto a la 3FN, hazte estas preguntas sobre cada columna que no forma parte de la clave:

  • ¿Esta columna describe directamente a la entidad principal que representa la tabla?
  • ¿Hay otra columna no clave de esta misma tabla que determine el valor de esta columna?
  • Si cambias el valor de esta columna en una fila, ¿tendrías que cambiarlo en otras filas también para mantener la consistencia?
  • ¿El mismo valor de esta columna aparece repetido en varias filas siempre que aparece el mismo valor de otra columna no clave?

Si alguna de las últimas tres respuestas es sí, hay una dependencia transitiva que resolver.

Conclusión

La Tercera Forma Normal completa el proceso de normalización básica. Con ella eliminas el último tipo de redundancia que las dos formas anteriores no detectan: los datos que dependen de otro dato no clave en lugar de depender directamente de la clave primaria.

Las tres formas normales juntas forman un sistema coherente:

  • 1FN: valores atómicos, sin grupos repetidos.
  • 2FN: cada columna depende de toda la clave, no solo de una parte.
  • 3FN: cada columna depende directamente de la clave, sin pasar por otra columna no clave.

Aplicar las tres de forma sistemática te garantiza un diseño libre de redundancias innecesarias, libre de anomalías de actualización, inserción y eliminación, y preparado para crecer de forma limpia y predecible.

Para afianzar los conceptos te recomendamos releer nuestros artículos sobre la Primera Forma Normal con ejemplos claros y sobre la Segunda Forma Normal explicada paso a paso, que forman junto a este artículo una trilogía completa sobre normalización.

Etiquetas: normalizacion

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a