Normalización de Bases de Datos: Qué es y por qué importa

D
DanisCh
(Actualizado: ) • 13 min de lectura
Normalización de Bases de Datos: Qué es y por qué importa
SQL

Diseñar una base de datos sin pensarla bien es como construir una casa sin planos. Al principio parece que funciona, pero con el tiempo empiezan a aparecer problemas: datos duplicados, actualizaciones inconsistentes, información que desaparece sin querer.

La normalización es el proceso que evita exactamente eso. Es un conjunto de reglas para organizar las tablas de una base de datos relacional de forma que los datos sean consistentes, sin redundancias y fáciles de mantener.

En este artículo vas a aprender qué es la normalización, por qué es tan importante y cómo aplicar las tres formas normales más usadas, todas con el mismo ejemplo para que puedas ver la evolución completa.


El problema que resuelve la normalización

Antes de hablar de soluciones, veamos el problema. Imagina que estás diseñando una base de datos para una tienda online y creas esta tabla para guardar los pedidos:

┌────────────┬──────────────┬─────────────────────┬──────────────┬─────────────────────┬───────────┬────────┐
│ pedido_id  │ cliente_id   │ cliente_nombre      │ cliente_email│ cliente_ciudad       │ producto   │ precio │
├────────────┼──────────────┼─────────────────────┼──────────────┼─────────────────────┼───────────┼────────┤
│     1     │    101       │ Ana García          │ ana@mail.com │ Madrid               │ Laptop    │ 899.99 │
│     2     │    101       │ Ana García          │ ana@mail.com │ Madrid               │ Mouse     │  29.99 │
│     3     │    101       │ Ana García          │ ana@mail.com │ Madrid               │ Teclado   │  79.99 │
│     4     │    102       │ Luis Pérez          │ luis@mail.com│ Barcelona            │ Monitor   │ 399.99 │
│     5     │    103       │ María López         │ maria@mail   │ Sevilla              │ Webcam    │  49.99 │
└────────────┴──────────────┴─────────────────────┴──────────────┴──────────────────────┴───────────┴────────┘

¿Ves el problema? Los datos de Ana García se repiten en tres filas. Si Ana cambia su email, hay que actualizar tres filas. Si te olvidas de una, la base de datos tendrá información contradictoria: una fila dirá que su email es ana@mail.com y otra dirá que es ana_nueva@mail.com. ¿Cuál es el correcto? Nadie lo sabe.

Esto genera lo que en bases de datos llamamos anomalías.


Las 3 anomalías que la normalización elimina

❌ Anomalía de actualización

Si Ana cambia su ciudad de Madrid a Valencia, necesitas actualizar todas las filas donde aparece Ana. Si fallas en alguna, tienes datos inconsistentes. Cuantos más pedidos tenga Ana, más filas debes actualizar y más riesgo de error.

❌ Anomalía de inserción

¿Qué pasa si quieres registrar un nuevo cliente que aún no ha hecho ningún pedido? Con esta estructura no puedes: no hay fila de pedido donde guardar sus datos. La información del cliente queda atada a la existencia de un pedido.

❌ Anomalía de eliminación

Si el pedido 5 de María López se cancela y lo eliminas, pierdes toda la información de María: nombre, email, ciudad. Todo desaparece con el pedido. Una acción de borrado afecta datos que no debería borrar.

La normalización resuelve estas tres anomalías dividiendo la información en tablas más pequeñas y bien organizadas, conectadas entre sí mediante claves.


Conceptos clave antes de empezar

Antes de ver las formas normales, necesitas tener claros estos conceptos:

  • 🔑 Clave primaria (PK) — columna o conjunto de columnas que identifica de forma única cada fila de una tabla
  • 🔗 Clave foránea (FK) — columna que referencia la clave primaria de otra tabla, estableciendo la relación entre ambas
  • 📐 Dependencia funcional — el campo B depende funcionalmente de A si para cada valor de A siempre hay un único valor de B. Por ejemplo: si conoces el cliente_id, siempre conoces el cliente_nombre → el nombre depende del ID
  • 🔢 Atomicidad — un campo es atómico si contiene un solo valor indivisible. "Madrid" es atómico. "Madrid, Barcelona, Sevilla" en un solo campo NO es atómico

Primera Forma Normal (1FN) — atomicidad y sin repetidos

Las reglas de la 1FN:

  1. Cada columna debe contener valores atómicos — un solo valor por celda, sin listas ni conjuntos
  2. Cada columna debe contener valores del mismo tipo
  3. Debe existir una clave primaria que identifique cada fila de forma única
  4. No debe haber columnas repetidas (como producto_1, producto_2, producto_3)

Ejemplo que viola la 1FN:

-- ❌ Esta tabla NO está en 1FN: el campo "productos" tiene múltiples valores
┌────────────┬──────────────┬──────────────────────────────────────────┐
│ pedido_id  │ cliente      │ productos                                │
├────────────┼──────────────┼──────────────────────────────────────────┤
│     1      │ Ana García   │ Laptop, Mouse, Teclado                   │
│     2      │ Luis Pérez   │ Monitor                                  │
│     3      │ María López  │ Webcam, Auriculares                      │
└────────────┴──────────────┴──────────────────────────────────────────┘
-- PROBLEMA: "Laptop, Mouse, Teclado" en una sola celda no es atómico
-- No se puede filtrar por un producto específico de forma eficiente

Aplicando la 1FN:

-- ✅ En 1FN: un producto por fila, cada celda tiene un valor atómico
┌────────────┬──────────────┬───────────────────┬─────────────────────┬───────────┬────────┐
│ pedido_id  │ cliente_id   │ cliente_nombre    │ cliente_email       │ producto  │ precio │
├────────────┼──────────────┼───────────────────┼─────────────────────┼───────────┼────────┤
│     1      │    101       │ Ana García        │ ana@mail.com        │ Laptop    │ 899.99 │
│     1      │    101       │ Ana García        │ ana@mail.com        │ Mouse     │  29.99 │
│     1      │    101       │ Ana García        │ ana@mail.com        │ Teclado   │  79.99 │
│     2      │    102       │ Luis Pérez        │ luis@mail.com       │ Monitor   │ 399.99 │
│     3      │    103       │ María López       │ maria@mail.com      │ Webcam    │  49.99 │
└────────────┴──────────────┴───────────────────┴─────────────────────┴───────────┴────────┘
-- Clave primaria compuesta: (pedido_id, producto) — la combinación identifica cada fila

Ahora está en 1FN: cada celda tiene un único valor. Sin embargo, los datos de Ana García siguen repitiéndose en tres filas. Eso lo resuelve la 2FN.

💡 Señal de que necesitas 1FN: si tienes campos como telefono1, telefono2, telefono3 o un campo con valores separados por comas.


Segunda Forma Normal (2FN) — sin dependencias parciales

La 2FN aplica cuando tienes una clave primaria compuesta (formada por dos o más columnas). Su regla es:

Las reglas de la 2FN:

  1. Debe estar en 1FN
  2. Todos los campos que no son clave deben depender de la clave completa, no solo de una parte de ella

En nuestra tabla, la clave primaria es (pedido_id, producto). Analicemos las dependencias:

Clave primaria: (pedido_id, producto)
                     ↑           ↑
                  parte A     parte B

Dependencias:
  cliente_id     → depende solo de pedido_id (parte A) ❌ dependencia PARCIAL
  cliente_nombre → depende solo de pedido_id (parte A) ❌ dependencia PARCIAL
  cliente_email  → depende solo de pedido_id (parte A) ❌ dependencia PARCIAL
  precio         → depende solo de producto   (parte B) ❌ dependencia PARCIAL

Ningún campo no-clave depende de la clave completa. ¡Todos son dependencias parciales! Esto viola la 2FN.

Aplicando la 2FN — dividir en tablas:

-- ✅ Tabla PEDIDOS — cada pedido con su cliente
┌────────────┬──────────────┐
│ pedido_id  │ cliente_id   │  ← clave primaria: pedido_id
├────────────┼──────────────┤
│     1      │    101       │
│     2      │    102       │
│     3      │    103       │
└────────────┴──────────────┘

-- ✅ Tabla CLIENTES — información de cada cliente una sola vez
┌──────────────┬──────────────────┬─────────────────┐
│ cliente_id   │ cliente_nombre   │ cliente_email   │  ← clave primaria: cliente_id
├──────────────┼──────────────────┼─────────────────┤
│    101       │ Ana García       │ ana@mail.com     │
│    102       │ Luis Pérez       │ luis@mail.com    │
│    103       │ María López      │ maria@mail.com   │
└──────────────┴──────────────────┴─────────────────┘

-- ✅ Tabla DETALLE_PEDIDOS — qué productos tiene cada pedido
┌────────────┬────────────┬────────┐
│ pedido_id  │ producto   │ precio │  ← clave primaria: (pedido_id, producto)
├────────────┼────────────┼────────┤
│     1      │ Laptop     │ 899.99 │
│     1      │ Mouse      │  29.99 │
│     1      │ Teclado    │  79.99 │
│     2      │ Monitor    │ 399.99 │
│     3      │ Webcam     │  49.99 │
└────────────┴────────────┴────────┘

Ahora si Ana cambia su email, solo hay que actualizar una fila en la tabla CLIENTES. Los pedidos no se ven afectados.

💡 Señal de que necesitas 2FN: si tienes una clave primaria compuesta y algunos campos no dependen de toda la clave sino solo de parte de ella.


Tercera Forma Normal (3FN) — sin dependencias transitivas

La 3FN elimina un tipo de redundancia más sutil: las dependencias entre campos que no son la clave primaria.

Las reglas de la 3FN:

  1. Debe estar en 2FN
  2. Ningún campo no-clave debe depender de otro campo no-clave (no debe haber dependencias transitivas)

Volvamos a nuestra tabla CLIENTES y añadamos un campo de ciudad y código postal:

-- Tabla CLIENTES ampliada
┌──────────────┬──────────────────┬─────────────────┬────────────┬────────────────┐
│ cliente_id   │ nombre           │ email           │ ciudad     │ codigo_postal  │
├──────────────┼──────────────────┼─────────────────┼────────────┼────────────────┤
│    101       │ Ana García       │ ana@mail.com     │ Madrid     │ 28001          │
│    102       │ Luis Pérez       │ luis@mail.com    │ Barcelona  │ 08001          │
│    103       │ María López      │ maria@mail.com   │ Sevilla    │ 41001          │
│    104       │ Carlos Ruiz      │ carlos@mail.com  │ Madrid     │ 28001          │
└──────────────┴──────────────────┴─────────────────┴────────────┴────────────────┘

Observa el problema: ciudad y codigo_postal están relacionados entre sí. Si sabes el código postal 28001, siempre sabes que es Madrid. La ciudad depende del código postal, no del cliente:

cliente_id → codigo_postal → ciudad
   ↑               ↑            ↑
 clave PK     campo no-clave  campo no-clave

La ciudad no depende directamente de cliente_id
sino de codigo_postal (dependencia TRANSITIVA) ❌

Si la ciudad de Madrid con código 28001 cambia su nombre (imposible en la realidad, pero sirve de ejemplo), tendrías que actualizar todas las filas con ese código postal.

Aplicando la 3FN — extraer la dependencia transitiva:

-- ✅ Tabla CLIENTES en 3FN
┌──────────────┬──────────────────┬─────────────────┬────────────────┐
│ cliente_id   │ nombre           │ email           │ codigo_postal  │
├──────────────┼──────────────────┼─────────────────┼────────────────┤
│    101       │ Ana García       │ ana@mail.com     │ 28001          │
│    102       │ Luis Pérez       │ luis@mail.com    │ 08001          │
│    103       │ María López      │ maria@mail.com   │ 41001          │
│    104       │ Carlos Ruiz      │ carlos@mail.com  │ 28001          │
└──────────────┴──────────────────┴─────────────────┴────────────────┘

-- ✅ Nueva tabla CODIGOS_POSTALES
┌────────────────┬────────────┐
│ codigo_postal  │ ciudad     │  ← clave primaria: codigo_postal
├────────────────┼────────────┤
│     28001      │ Madrid     │
│     08001      │ Barcelona  │
│     41001      │ Sevilla    │
└────────────────┴────────────┘

Ahora si dos clientes tienen el mismo código postal, la ciudad se almacena una sola vez. Para saber la ciudad de un cliente, simplemente haces JOIN con la tabla de códigos postales.

💡 Señal de que necesitas 3FN: si tienes un campo A que determina un campo B, y B determina un campo C, pero C no depende directamente de A. La cadena A → B → C es una dependencia transitiva que hay que separar.


El resultado final — todas las tablas normalizadas

Después de aplicar 1FN, 2FN y 3FN, la base de datos queda así:

-- ✅ CLIENTES (3FN)
CREATE TABLE clientes (
  cliente_id     INT 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_postal)
);

-- ✅ CODIGOS_POSTALES (separada por 3FN)
CREATE TABLE codigos_postales (
  codigo_postal  CHAR(5) PRIMARY KEY,
  ciudad         VARCHAR(100) NOT NULL
);

-- ✅ PRODUCTOS (separada por 2FN)
CREATE TABLE productos (
  producto_id    INT PRIMARY KEY,
  nombre         VARCHAR(100) NOT NULL,
  precio_base    DECIMAL(10,2) NOT NULL
);

-- ✅ PEDIDOS (separada por 2FN)
CREATE TABLE pedidos (
  pedido_id      INT PRIMARY KEY,
  cliente_id     INT NOT NULL,
  fecha          DATE NOT NULL,
  FOREIGN KEY (cliente_id) REFERENCES clientes(cliente_id)
);

-- ✅ DETALLE_PEDIDOS (tabla de relación muchos-a-muchos)
CREATE TABLE detalle_pedidos (
  pedido_id      INT,
  producto_id    INT,
  cantidad       INT NOT NULL DEFAULT 1,
  precio_unitario DECIMAL(10,2) NOT NULL,
  PRIMARY KEY (pedido_id, producto_id),
  FOREIGN KEY (pedido_id)  REFERENCES pedidos(pedido_id),
  FOREIGN KEY (producto_id) REFERENCES productos(producto_id)
);

Ahora para consultar un pedido completo con toda la información:

SELECT
  p.pedido_id,
  p.fecha,
  c.nombre      AS cliente,
  c.email,
  cp.ciudad,
  pr.nombre     AS producto,
  dp.cantidad,
  dp.precio_unitario,
  (dp.cantidad * dp.precio_unitario) AS subtotal
FROM pedidos p
JOIN clientes c          ON p.cliente_id  = c.cliente_id
JOIN codigos_postales cp ON c.codigo_postal = cp.codigo_postal
JOIN detalle_pedidos dp  ON p.pedido_id   = dp.pedido_id
JOIN productos pr        ON dp.producto_id = pr.producto_id
WHERE p.pedido_id = 1;

La Forma Normal de Boyce-Codd (BCNF)

La BCNF es una versión más estricta de la 3FN que cubre algunos casos especiales que 3FN no contempla. Solo la necesitas cuando tienes múltiples claves candidatas que se superponen.

Para la mayoría de los proyectos prácticos, llegar a 3FN es suficiente. La BCNF y las formas normales 4FN y 5FN existen para casos muy específicos y complejos que raramente se dan en aplicaciones convencionales.


Normalización vs Desnormalización — el equilibrio práctico

Aquí hay algo que muchos artículos no te dicen: la normalización total no siempre es la mejor opción.

Normalizar significa más tablas, y más tablas significa más JOINs en las consultas. En bases de datos con millones de filas, demasiados JOINs pueden hacer que las consultas sean lentas.

Por eso existe la desnormalización: el proceso inverso, donde conscientemente introduces algo de redundancia controlada para mejorar el rendimiento de consultas frecuentes.

-- Tabla desnormalizada para reportes rápidos
-- (redundante pero eficiente para consultas de lectura masiva)
CREATE TABLE reporte_ventas_diarias (
  fecha          DATE,
  producto_nombre VARCHAR(100),  -- redundante: podría ir en tabla productos
  ciudad          VARCHAR(100),  -- redundante: podría ir en tabla ciudades
  total_vendido   DECIMAL(10,2),
  unidades        INT
);
-- Esta tabla se actualiza periódicamente (ETL)
-- Las consultas de reporte son instantáneas sin JOINs

La regla general del mundo profesional: normaliza para escribir, desnormaliza para leer. Usa 3FN para las tablas operacionales del día a día y considera desnormalizar para tablas de reportes o analíticas.


Resumen visual: las 3 formas normales

Forma NormalProblema que resuelveRegla principalSeñal de que la necesitas
1FNValores no atómicos y columnas repetidasCada celda contiene un único valor indivisibleCampos con listas separadas por comas o columnas tipo campo_1, campo_2...
2FNDependencias parciales con clave compuestaTodos los campos dependen de la clave completaTienes clave primaria compuesta y campos que solo dependen de parte de ella
3FNDependencias transitivas entre camposNingún campo no-clave depende de otro campo no-claveUn campo determina a otro campo que no es clave (A → B → C)
BCNFCasos especiales de 3FN con múltiples claves candidatasToda dependencia funcional tiene como determinante una superclaveMúltiples claves candidatas que se solapan (poco frecuente)

Errores comunes al normalizar

Normalizar en exceso

No todo necesita estar separado en una tabla. Si una ciudad siempre va ligada a un único código postal y nunca va a cambiar de forma independiente, separarlos puede ser overkill. Usa el sentido común junto con las reglas formales.

Olvidar los índices en las claves foráneas

-- ❌ Sin índice, el JOIN es lento en tablas grandes
FOREIGN KEY (cliente_id) REFERENCES clientes(cliente_id)

-- ✅ Añade índice en la columna de clave foránea
CREATE INDEX idx_pedidos_cliente ON pedidos(cliente_id);

No definir correctamente las claves primarias

Una tabla sin clave primaria bien definida no puede normalizarse correctamente. Antes de normalizar, identifica qué campo o combinación de campos identifica de forma única cada fila.

Confundir 2FN con 3FN

La 2FN habla de dependencias respecto a la clave primaria. La 3FN habla de dependencias entre campos no-clave. Son problemas diferentes con soluciones diferentes.


Resumen: lo que aprendiste hoy

  • ✅ La normalización organiza tablas para eliminar redundancias y anomalías de datos
  • ✅ Hay tres tipos de anomalías: de actualización, de inserción y de eliminación
  • ✅ 1FN — cada celda contiene un valor atómico, no listas ni grupos repetidos
  • ✅ 2FN — todos los campos dependen de la clave completa (no de una parte)
  • ✅ 3FN — ningún campo no-clave depende de otro campo no-clave
  • ✅ Las formas normales son acumulativas: para estar en 3FN, debes cumplir 1FN y 2FN también
  • ✅ En la práctica, llegar a 3FN es suficiente para la mayoría de aplicaciones
  • ✅ La desnormalización controlada puede ser útil para mejorar el rendimiento de consultas de lectura
  • ✅ El proceso fue definido por E.F. Codd en 1972 y sigue siendo estándar hoy en día

🧪 ¿Tienes bien sólidos tus fundamentos de SQL?

La normalización se aplica al diseñar bases de datos relacionales con SQL. Para sacarle todo el partido necesitas dominar los conceptos de claves, JOINs y consultas. Comprueba dónde estás:

👉 Test: SQL Básico 👉 Test: SQL vs NoSQL 

¿Cuál forma normal te costó más entender: la 2FN con las dependencias parciales o la 3FN con las transitivas? ¿Ya aplicabas normalización sin saberlo o era la primera vez que veías estos conceptos? Cuéntanos en los comentarios 👇 — respondemos todos. 🚀

¿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