Normalización de Bases de Datos — 1NF a 5NF Explicado
Guía visual de normalización de bases de datos: aprende 1NF a 5NF con ejemplos prácticos, cuándo aplicar cada forma y cómo balancear normalización con rendimiento.
Overview
La normalización toma una tabla cruda — donde el mismo dato vive en veinte filas y actualizar un sitio rompe otro — y la divide en tablas más pequeñas que describen una sola cosa cada una. Las formas normales (de 1NF a 5NF) son una escalera de reglas que elimina un tipo concreto de redundancia en cada peldaño. La mayoría de los esquemas en producción paran en 3NF o BCNF; las formas altas importan para entrevistas, casos límite raros y saber exactamente qué sacrificas cuando desnormalizas a propósito.
Cuándo usarla
- Diseñar esquemas relacionales desde cero
- Refactorizar bases de datos legacy con datos duplicados
- Preparar esquemas para cargas transaccionales (OLTP)
- Antes de decidir qué desnormalizar para reportes (OLAP) — mira la Guía de Desnormalización para la otra cara del trade-off
1NF — Valores Atómicos
Regla: Cada columna contiene solo valores atómicos (indivisibles). Sin grupos repetidos.
Antes — un grupo repetido en una columna:
| order_id | customer | products |
|---|---|---|
| 1 | Alice | Apple, Banana, Cherry |
Después — solo valores atómicos:
| order_id | customer | product |
|---|---|---|
| 1 | Alice | Apple |
| 1 | Alice | Banana |
| 1 | Alice | Cherry |
2NF — Sin Dependencias Parciales
Regla: Todos los atributos no-clave dependen de la clave primaria completa (relevante para claves compuestas).
Antes — depende de parte de la clave:
| course_id | student_id | course_name | student_name | grade |
|---|---|---|---|---|
| CS101 | S1 | Intro to CS | Alice | A |
course_name depende solo de course_id; student_name solo de student_id.
Después — todo depende de la clave completa:
Enrollments:
| course_id | student_id | grade |
|---|---|---|
| CS101 | S1 | A |
Courses:
| course_id | course_name |
|---|---|
| CS101 | Intro to CS |
Students:
| student_id | student_name |
|---|---|
| S1 | Alice |
3NF — Sin Dependencias Transitivas
Regla: Los atributos no-clave dependen solo de la clave primaria, no de otros atributos no-clave.
Antes — una columna no clave determina otra:
| employee_id | name | department_id | department_name | department_head |
|---|---|---|---|---|
| E1 | Bob | D1 | Engineering | Carol |
department_name y department_head dependen de department_id, no de employee_id.
Después — hechos sobre la clave, nada más:
Employees:
| employee_id | name | department_id |
|---|---|---|
| E1 | Bob | D1 |
Departments:
| department_id | department_name | department_head |
|---|---|---|
| D1 | Engineering | Carol |
BCNF — Forma Normal de Boyce-Codd
Regla: Para toda dependencia funcional X → Y, X debe ser superclave.
Antes — un determinante que no es clave:
| student | course | professor |
|---|---|---|
| Alice | CS101 | Prof. Smith |
| Bob | CS101 | Prof. Smith |
course → professor, pero course no es superclave.
Después — todo determinante es clave:
Enrollments:
| student | course |
|---|---|
| Alice | CS101 |
| Bob | CS101 |
CourseAssignments:
| course | professor |
|---|---|
| CS101 | Prof. Smith |
4NF — Sin Dependencias Multivaluadas
Regla: Sin dependencias multivaluadas excepto sobre superclaves.
Antes — dos hechos independientes por fila:
| employee | skill | language |
|---|---|---|
| Alice | Java | English |
| Alice | Java | Spanish |
| Alice | Python | English |
| Alice | Python | Spanish |
Skills y languages son hechos multivaluados independientes.
Después — los hechos independientes van separados:
EmployeeSkills:
| employee | skill |
|---|---|
| Alice | Java |
| Alice | Python |
EmployeeLanguages:
| employee | language |
|---|---|
| Alice | English |
| Alice | Spanish |
5NF — Dependencia de Unión
Regla: Toda dependencia de unión está implicada por las claves candidatas.
Antes — un trío combinable forzado en pares:
| agent | company | product |
|---|---|---|
| Smith | Ford | Truck |
| Smith | Ford | Car |
| Smith | Toyota | Car |
| Jones | Toyota | Car |
Después — descompuesto según la dependencia de join:
AgentCompany:
| agent | company |
|---|---|
| Smith | Ford |
| Smith | Toyota |
| Jones | Toyota |
AgentProduct:
| agent | product |
|---|---|
| Smith | Truck |
| Smith | Car |
| Jones | Car |
CompanyProduct:
| company | product |
|---|---|
| Ford | Truck |
| Ford | Car |
| Toyota | Car |
Resumen de Normalización
| Forma | Regla | Elimina |
|---|---|---|
| 1NF | Valores atómicos | Grupos repetidos |
| 2NF | Dependencia de clave completa | Dependencias parciales |
| 3NF | Dependencia solo de clave | Dependencias transitivas |
| BCNF | Determinante superclave | Anomalías restantes |
| 4NF | Sin dependencias multivaluadas | Valores multi-independientes |
| 5NF | Dependencias de unión | Uniones reconstruibles |
Cada paso elimina una clase de redundancia — para donde tu carga de trabajo deje de necesitarlo.
Cuándo Detener la Normalización
- 3NF/BCNF es el punto práctico para la mayoría de sistemas OLTP
- 4NF importa cuando tienes atributos verdaderamente multivaluados (raro)
- 5NF es mayormente teórico para aplicaciones productivas
- Desnormaliza intencionalmente cuando el rendimiento de lectura importa más que la integridad de escritura — la guía de desnormalización cubre los patrones y las estrategias de sincronización
Errores Comunes
- Sobrenormalizar hasta 5NF — añade complejidad con mínimo beneficio práctico
- Subnormalizar hasta 1NF — lleva a anomalías de actualización e inconsistencia de datos
- Normalizar antes de entender las queries — el esquema debe servir la carga de trabajo
- Ignorar BCNF — 3NF no maneja todas las anomalías; BCNF es el estándar más estricto
Ejemplo: Pasos de Normalizacion
-- 1NF: Eliminar grupos repetitivos
-- Desnormalizado: orders(id, customer_name, items_csv)
-- 1NF: orders(id, customer_name, item_name, qty)
-- 2NF: Eliminar dependencias parciales (clave compuesta)
-- 1NF: order_items(order_id, product_id, product_name, qty)
-- 2NF: orders(order_id, customer_id)
-- products(product_id, product_name)
-- order_items(order_id, product_id, qty)
-- 3NF: Eliminar dependencias transitivas
-- 2NF: orders(order_id, customer_id, customer_name, customer_city)
-- 3NF: orders(order_id, customer_id)
-- customers(customer_id, customer_name, customer_city)
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
customer_name VARCHAR(200) NOT NULL,
customer_city VARCHAR(100)
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id),
order_date DATE NOT NULL DEFAULT CURRENT_DATE
);
CREATE TABLE order_items (
order_id INT REFERENCES orders(order_id),
product_id INT REFERENCES products(product_id),
qty INT NOT NULL CHECK (qty > 0),
PRIMARY KEY (order_id, product_id)
); Recursos Relacionados
Desnormalización: espacio por velocidad de lectura
Guía práctica de desnormalización de bases de datos: cuándo cambiar almacenamiento por rendimiento de lectura y mantener datos consistentes.
GuideSQL Joins — Guía Visual con Ejemplos
Guía visual de SQL joins: INNER, LEFT, RIGHT, FULL OUTER, CROSS y SELF joins con ejemplos prácticos, tips de rendimiento y errores comunes.
GuideEstrategias de Indexación — Desde B-Trees hasta BRIN
Guía práctica de índices de bases de datos: B-Trees, Hash, GIN, GiST, BRIN e índices parciales. Aprende cuándo usar cada uno y cómo evitar errores comunes de indexación.
RecipeMigraciones de Base de Datos con Flyway, Alembic y Liquibase
Cómo versionar, aplicar y revertir cambios de esquema de base de datos de forma segura con Flyway, Alembic, Liquibase y Sequelize — incluye expand-contract y patrones sin downtime.
RecipeCRUD con ORM: SQLAlchemy, Prisma y Hibernate
Cómo ejecutar operaciones de creación, lectura, actualización y borrado con SQLAlchemy, Prisma y Hibernate: el mismo modelo de datos en Python, JavaScript y Java.
GuideACID vs BASE — Modelos de Consistencia Explicados
Guía práctica que compara los modelos de consistencia ACID y BASE: cuándo elegir consistencia fuerte, cuándo aceptar la eventual y cómo afecta al diseño.