La Segunda Forma Normal explicada paso a paso con ejemplos

D
DanisCh
• 12 min de lectura
La Segunda Forma Normal explicada paso a paso con ejemplos
Conceptos de Base de Datos

Ya sabes que la Primera Forma Normal exige que cada celda tenga un solo valor y que no haya columnas repetidas. Con eso resuelves los problemas más visibles de una tabla mal diseñada. Pero hay un tipo de redundancia más sutil que la 1FN no elimina, y que puede causar exactamente los mismos problemas: datos duplicados, actualizaciones inconsistentes y pérdida de información al borrar registros.

Ese problema lo resuelve la Segunda Forma Normal (2FN).

En este artículo aprenderás qué es la 2FN, cuándo se aplica, cómo detectar cuándo una tabla la viola y cómo corregirlo, todo con ejemplos concretos y visuales pensados para que quede completamente claro.

El concepto que necesitas entender antes: dependencia funcional

Para entender la 2FN necesitas entender qué es una dependencia funcional. Es más sencillo de lo que suena.

Decimos que una columna B depende funcionalmente de una columna A cuando: si conoces el valor de A, puedes determinar de forma única el valor de B. Lo escribimos así: A → B (A determina B).

Ejemplos cotidianos:

  • id_producto → nombre_producto: si sabes el ID del producto, sabes su nombre. Un ID solo puede corresponder a un nombre.
  • id_cliente → email_cliente: si sabes el ID del cliente, sabes su email.
  • codigo_postal → ciudad: si sabes el código postal, sabes la ciudad a la que pertenece.

Ahora imagina que la clave primaria de una tabla está formada por dos columnas juntas, por ejemplo (id_pedido, id_producto). Esa combinación identifica de forma única cada fila. Pero ¿qué pasa si una columna de esa tabla solo depende de una parte de la clave, no de la combinación completa? Eso se llama dependencia parcial, y es exactamente lo que la 2FN prohíbe.

Definición de la Segunda Forma Normal

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

  • Ya está en Primera Forma Normal.
  • Todos los atributos que no forman parte de la clave primaria dependen de toda la clave primaria, no solo de una parte de ella.

Dicho de otra forma: si tienes una clave primaria compuesta por varias columnas, ningún otro dato de la tabla puede depender solo de una de esas columnas. Debe depender de todas juntas.

Hay un detalle importante: la 2FN solo es relevante cuando la clave primaria está compuesta por más de una columna. Si tu tabla tiene una clave primaria de una sola columna (como un ID autoincremental) y ya está en 1FN, automáticamente cumple la 2FN. No hay partes en una clave de un solo campo, así que no puede haber dependencias parciales.

Un ejemplo que lo hace todo más claro

Imagina una tabla que registra los productos incluidos en cada pedido. La clave primaria es la combinación (id_pedido, id_producto) porque un mismo producto puede aparecer en muchos pedidos, y un pedido puede tener muchos productos.

Tabla detalle_pedidos (viola la 2FN):

id_pedido (PK)id_producto (PK)cantidadnombre_productoprecio_productonombre_clienteemail_cliente
11012Laptop Pro1299.99Ana Garcíaana@mail.com
11021Mouse45.50Ana Garcíaana@mail.com
21011Laptop Pro1299.99Luis Pérezluis@mail.com
21033Teclado89.99Luis Pérezluis@mail.com
31022Mouse45.50Ana Garcíaana@mail.com

A primera vista puede parecer razonable. Pero analicemos las dependencias funcionales de cada columna respecto a la clave primaria (id_pedido, id_producto):

  • (id_pedido, id_producto) → cantidad: la cantidad de ese producto en ese pedido específico depende de los dos. ✅ Dependencia completa.
  • id_producto → nombre_producto: el nombre del producto solo depende del ID del producto, no del pedido. ❌ Dependencia parcial.
  • id_producto → precio_producto: el precio del producto solo depende del ID del producto. ❌ Dependencia parcial.
  • id_pedido → nombre_cliente: el nombre del cliente solo depende del pedido. ❌ Dependencia parcial.
  • id_pedido → email_cliente: el email del cliente solo depende del pedido. ❌ Dependencia parcial.

De cinco columnas adicionales, cuatro tienen dependencias parciales. La tabla viola la 2FN de forma severa.

Los problemas concretos que causa esta violación

No es solo una cuestión teórica. Esas dependencias parciales generan problemas reales:

Anomalía de actualización

El producto con id_producto = 101 (Laptop Pro) aparece en dos filas con el mismo nombre y precio. Si ese producto cambia de nombre, tienes que actualizar todas las filas donde aparece. Si actualizas solo algunas, la base de datos tendrá datos contradictorios: el mismo producto con dos nombres diferentes.

-- ❌ Problema: si el nombre del producto cambia,
-- hay que actualizar todas las filas que lo mencionan
UPDATE detalle_pedidos
SET nombre_producto = 'Laptop Pro 2025'
WHERE id_producto = 101;
-- ¿Cuántas filas hay que actualizar? ¿Y si se te olvida alguna?

Anomalía de inserción

¿Puedes registrar un nuevo producto en el catálogo antes de que aparezca en algún pedido? No. Esta tabla solo permite agregar productos cuando ya están dentro de un pedido. Si quieres crear el catálogo de productos independientemente, no puedes.

Anomalía de eliminación

Si el pedido número 3 se cancela y eliminas sus filas, también pierdes el registro de que Ana García existe como cliente. Si ese era el único pedido que tenía con ese email, esa información desaparece junto con el pedido.

-- ❌ Problema: eliminar el pedido 3 hace desaparecer datos del cliente
DELETE FROM detalle_pedidos WHERE id_pedido = 3;
-- Ana García desaparece si no tenía otros pedidos

Cómo aplicar la 2FN: separar según de qué depende cada columna

La solución es mecánica una vez identificadas las dependencias: cada grupo de columnas que depende de la misma clave va en su propia tabla. Veamos el proceso paso a paso.

Paso 1: identificar todos los grupos de dependencia

  • Dependen de (id_pedido, id_producto) juntos: cantidad
  • Dependen solo de id_producto: nombre_producto, precio_producto
  • Dependen solo de id_pedido: nombre_cliente, email_cliente

Paso 2: crear una tabla para cada grupo

Tabla productos (columnas que dependen de id_producto):

id_producto (PK)nombre_productoprecio_producto
101Laptop Pro1299.99
102Mouse45.50
103Teclado89.99

Tabla pedidos (columnas que dependen de id_pedido):

id_pedido (PK)nombre_clienteemail_cliente
1Ana Garcíaana@mail.com
2Luis Pérezluis@mail.com
3Ana Garcíaana@mail.com

Tabla detalle_pedidos (columna que depende de la clave compuesta completa):

id_pedido (PK)id_producto (PK)cantidad
11012
11021
21011
21033
31022

Ahora cada tabla tiene exactamente lo que le corresponde. Los problemas anteriores desaparecen:

  • Cambiar el nombre de un producto: una sola fila en la tabla productos.
  • Agregar un producto al catálogo sin que esté en ningún pedido: perfectamente posible insertando en productos.
  • Eliminar el pedido 3: no se pierde ningún dato del cliente porque el cliente tiene su propia tabla (o en una mejora posterior, una tabla clientes separada).

Segundo ejemplo: tabla de notas de estudiantes

Otro caso muy ilustrativo. Una tabla que registra las notas de los estudiantes en distintas asignaturas:

Tabla notas (viola la 2FN):

id_estudiante (PK)id_asignatura (PK)notanombre_estudiantecarrera_estudiantenombre_asignaturacreditos_asignaturadepartamento
E001A108.5Laura MartínIngenieríaBases de Datos6Informática
E001A117.0Laura MartínIngenieríaRedes4Informática
E002A109.0Marco TorresTelemáticaBases de Datos6Informática
E002A126.5Marco TorresTelemáticaMatemáticas8Ciencias

Análisis de dependencias con clave (id_estudiante, id_asignatura):

  • (id_estudiante, id_asignatura) → nota: la nota depende de ambos. Un estudiante puede tener distinta nota en cada asignatura. ✅ Completa.
  • id_estudiante → nombre_estudiante: el nombre depende solo del estudiante. ❌ Parcial.
  • id_estudiante → carrera_estudiante: la carrera depende solo del estudiante. ❌ Parcial.
  • id_asignatura → nombre_asignatura: el nombre depende solo de la asignatura. ❌ Parcial.
  • id_asignatura → creditos_asignatura: los créditos dependen solo de la asignatura. ❌ Parcial.
  • id_asignatura → departamento: el departamento depende solo de la asignatura. ❌ Parcial.

De seis columnas, cinco tienen dependencias parciales. Apliquemos la 2FN:

Tabla estudiantes:

id_estudiante (PK)nombrecarrera
E001Laura MartínIngeniería
E002Marco TorresTelemática

Tabla asignaturas:

id_asignatura (PK)nombrecreditosdepartamento
A10Bases de Datos6Informática
A11Redes4Informática
A12Matemáticas8Ciencias

Tabla notas (solo lo que depende de ambas claves):

id_estudiante (PK)id_asignatura (PK)nota
E001A108.5
E001A117.0
E002A109.0
E002A126.5

Ahora los datos de los estudiantes y las asignaturas se guardan una sola vez. Consultar las notas con contexto sigue siendo sencillo con un JOIN:

SELECT
  e.nombre   AS estudiante,
  a.nombre   AS asignatura,
  a.creditos,
  n.nota
FROM notas n
JOIN estudiantes e  ON e.id_estudiante  = n.id_estudiante
JOIN asignaturas a  ON a.id_asignatura  = n.id_asignatura
ORDER BY e.nombre, a.nombre;

Tercer ejemplo: reservas de hotel

Un caso práctico de un sistema de reservas. Cuando alguien reserva una habitación, se registra así:

Tabla reservas (viola la 2FN):

id_reserva (PK)id_habitacion (PK)fecha_entradafecha_salidatipo_habitacionprecio_nochecapacidadnombre_huespedemail_huesped
R0011012024-07-102024-07-13Doble120.002Carmen Vegacarmen@mail.com
R0022052024-07-152024-07-17Suite280.003Roberto Gilroberto@mail.com
R0031012024-08-012024-08-05Doble120.002Ana Garcíaana@mail.com

Dependencias con clave (id_reserva, id_habitacion):

  • (id_reserva, id_habitacion) → fecha_entrada, fecha_salida: las fechas de una reserva en una habitación concreta dependen de ambas. ✅ Completa.
  • id_habitacion → tipo_habitacion: el tipo de habitación no cambia por cada reserva. ❌ Parcial.
  • id_habitacion → precio_noche: el precio por noche es una característica de la habitación. ❌ Parcial.
  • id_habitacion → capacidad: la capacidad es fija en la habitación. ❌ Parcial.
  • id_reserva → nombre_huesped: el huésped depende de la reserva, no de la habitación. ❌ Parcial.
  • id_reserva → email_huesped: igual que el anterior. ❌ Parcial.

Aplicando la 2FN, separamos en tres tablas:

Tabla habitaciones:

id_habitacion (PK)tipoprecio_nochecapacidad
101Doble120.002
205Suite280.003

Tabla huespedes:

id_reserva (PK)nombreemail
R001Carmen Vegacarmen@mail.com
R002Roberto Gilroberto@mail.com
R003Ana Garcíaana@mail.com

Tabla reservas (solo lo que depende de ambas claves):

id_reserva (PK)id_habitacion (PK)fecha_entradafecha_salida
R0011012024-07-102024-07-13
R0022052024-07-152024-07-17
R0031012024-08-012024-08-05

Ahora si el precio de la habitación 101 sube de 120 a 135 euros por noche, se actualiza en un solo lugar. Las reservas históricas que ya guardaron el precio que pagó el cliente en su momento no se ven afectadas.

El diagrama mental para detectar violaciones

Cuando tengas una tabla con clave primaria compuesta, hazte esta pregunta por cada columna que no forma parte de la clave:

¿Esta columna describe a la fila completa (combinación de ambas claves) o describe solo a uno de los elementos de la clave por separado?

  • Si describe la fila completa → dependencia completa. ✅ Está bien donde está.
  • Si describe solo uno de los elementos → dependencia parcial. ❌ Debe ir a una tabla separada.

En la tabla de notas:

  • ¿La nota describe al estudiante en esa asignatura (fila completa)? Sí. ✅
  • ¿El nombre del estudiante describe al estudiante en esa asignatura? No, describe al estudiante sin importar la asignatura. ❌
  • ¿Los créditos de la asignatura describen al estudiante en esa asignatura? No, describen a la asignatura sin importar el estudiante. ❌

El proceso completo en SQL

Veamos cómo queda el ejemplo del pedido en SQL correcto desde el principio:

-- Tabla de clientes (entidad propia)
CREATE TABLE clientes (
  id     INT          AUTO_INCREMENT PRIMARY KEY,
  nombre VARCHAR(100) NOT NULL,
  email  VARCHAR(100) UNIQUE NOT NULL
);

-- Tabla de productos (entidad propia)
CREATE TABLE productos (
  id      INT            AUTO_INCREMENT PRIMARY KEY,
  nombre  VARCHAR(100)   NOT NULL,
  precio  DECIMAL(10, 2) NOT NULL,
  stock   INT            DEFAULT 0
);

-- Tabla de pedidos (vincula un cliente con su pedido)
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 detalle: 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,  -- precio en el momento del pedido
  PRIMARY KEY (id_pedido, id_producto),
  FOREIGN KEY (id_pedido)  REFERENCES pedidos(id),
  FOREIGN KEY (id_producto) REFERENCES productos(id)
);

Nota que precio_unit sí pertenece en detalle_pedidos aunque el precio también esté en productos. ¿Por qué? Porque el precio que pagó el cliente en ese pedido es un hecho histórico de esa transacción específica. Si mañana el producto sube de precio, los pedidos anteriores deben conservar el precio que se cobró en su momento. Ese precio depende de la combinación (id_pedido, id_producto), no solo del producto.

Resumen visual del proceso

ColumnaDepende deTipo de dependencia¿Dónde va?
cantidad(id_pedido, id_producto)✅ Completadetalle_pedidos
precio_unit(id_pedido, id_producto)✅ Completadetalle_pedidos
nombre_productoid_producto❌ Parcialproductos
precio_productoid_producto❌ Parcialproductos
nombre_clienteid_pedido❌ Parcialpedidos / clientes
email_clienteid_pedido❌ Parcialpedidos / clientes

Lista de verificación para la 2FN

Antes de dar una tabla por válida respecto a la 2FN, hazte estas preguntas:

  • ¿La clave primaria está formada por más de una columna? Si no, la tabla ya cumple la 2FN automáticamente (si está en 1FN).
  • ¿Hay alguna columna que describe a uno de los elementos de la clave por sí solo, sin necesitar el otro?
  • ¿Hay datos que se repiten en varias filas porque siempre van acompañados de la misma parte de la clave?
  • ¿Si cambias un dato de un elemento (producto, estudiante, habitación) tienes que actualizarlo en múltiples filas?

Si alguna de las últimas tres preguntas tiene respuesta afirmativa, hay dependencias parciales que resolver.

Conclusión

La Segunda Forma Normal elimina un tipo de redundancia invisible a primera vista pero con consecuencias reales: las dependencias parciales. Cuando una tabla tiene clave primaria compuesta y algunas columnas solo dependen de parte de esa clave, los datos se repiten, las actualizaciones se vuelven peligrosas y la integridad del sistema se compromete.

El proceso para aplicarla es siempre el mismo: identificar de qué depende cada columna, agrupar las que comparten la misma dependencia y crear una tabla para cada grupo. El resultado es un esquema más limpio, con menos redundancia y mucho más fácil de mantener.

Recuerda que la 2FN es un paso intermedio. Una vez que tu diseño la cumple, el siguiente objetivo es aplicar la Tercera Forma Normal para eliminar las dependencias transitivas, el otro tipo de redundancia que aún puede existir aunque ya hayas aplicado la 1FN y la 2FN. Y si quieres afianzar la base antes de continuar, te recomendamos releer el artículo sobre la Primera Forma Normal con ejemplos claros.

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