La normalización es el proceso de organizar una base de datos para eliminar redundancia y dependencias problemáticas. Una tabla mal diseñada puede corromperse sola con el tiempo — la normalización previene eso con reglas matemáticas concretas.

El problema sin normalización

Imagina una tabla que guarda pedidos con toda la información del cliente en cada fila:

| pedido_id | cliente_nombre | cliente_email     | cliente_ciudad | producto      | precio |
|-----------|----------------|-------------------|----------------|---------------|--------|
| 1         | Juan Pérez     | juan@ej.com       | Guadalajara    | Cable HDMI    | 150.00 |
| 2         | Juan Pérez     | juan@ej.com       | Guadalajara    | Mouse USB     | 200.00 |
| 3         | Ana García     | ana@ej.com        | CDMX           | Teclado       | 350.00 |

Problemas evidentes:

  • Anomalía de actualización: Si Juan cambia su email, hay que actualizar múltiples filas
  • Anomalía de inserción: No puedo agregar un cliente sin que tenga un pedido
  • Anomalía de eliminación: Si elimino el único pedido de Ana, pierdo sus datos como cliente
  • Redundancia: El nombre y ciudad de Juan se repite N veces

Primera Forma Normal (1NF)

Regla: cada celda contiene un valor atómico (indivisible) y no hay grupos repetidos.

-- MAL: la columna "telefonos" tiene múltiples valores
| cliente_id | nombre     | telefonos              |
|------------|------------|------------------------|
| 1          | Juan Pérez | 333-1234, 333-5678     |

-- BIEN: tabla separada para teléfonos
CREATE TABLE clientes (
    id INT PRIMARY KEY AUTO_INCREMENT,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(150) UNIQUE NOT NULL
);

CREATE TABLE telefonos_clientes (
    id INT PRIMARY KEY AUTO_INCREMENT,
    cliente_id INT NOT NULL,
    telefono VARCHAR(20) NOT NULL,
    tipo ENUM('celular', 'fijo', 'trabajo') DEFAULT 'celular',
    FOREIGN KEY (cliente_id) REFERENCES clientes(id) ON DELETE CASCADE
);

Segunda Forma Normal (2NF)

Regla: debe estar en 1NF y todos los atributos no-clave deben depender de la clave primaria completa (aplica cuando la clave primaria es compuesta).

-- MAL: tabla de inscripciones donde precio_materia depende solo de materia_id
-- (no de la clave compuesta alumno_id + materia_id)
CREATE TABLE inscripciones_mal (
    alumno_id   INT,
    materia_id  INT,
    calificacion DECIMAL(4,2),
    precio_materia DECIMAL(10,2),   -- ← depende solo de materia_id, no del par
    PRIMARY KEY (alumno_id, materia_id)
);

-- BIEN: precio_materia va en su propia tabla
CREATE TABLE materias (
    id     INT PRIMARY KEY AUTO_INCREMENT,
    nombre VARCHAR(100) NOT NULL,
    precio DECIMAL(10,2) NOT NULL
);

CREATE TABLE inscripciones (
    alumno_id   INT,
    materia_id  INT,
    calificacion DECIMAL(4,2),
    PRIMARY KEY (alumno_id, materia_id),
    FOREIGN KEY (alumno_id)  REFERENCES alumnos(id),
    FOREIGN KEY (materia_id) REFERENCES materias(id)
);

Tercera Forma Normal (3NF)

Regla: debe estar en 2NF y ningún atributo no-clave debe depender de otro atributo no-clave (sin dependencias transitivas).

-- MAL: ciudad_cp depende de codigo_postal, no de cliente_id
CREATE TABLE clientes_mal (
    id           INT PRIMARY KEY,
    nombre       VARCHAR(100),
    codigo_postal VARCHAR(10),
    ciudad_cp    VARCHAR(100)    -- ← depende de codigo_postal, no del id
);

-- BIEN: extraer la dependencia transitiva
CREATE TABLE codigos_postales (
    codigo_postal VARCHAR(10) PRIMARY KEY,
    ciudad        VARCHAR(100) NOT NULL,
    estado        VARCHAR(100) NOT NULL
);

CREATE TABLE clientes (
    id           INT PRIMARY KEY AUTO_INCREMENT,
    nombre       VARCHAR(100) NOT NULL,
    codigo_postal VARCHAR(10),
    FOREIGN KEY (codigo_postal) REFERENCES codigos_postales(codigo_postal)
);

¿Cuándo desnormalizar?

La normalización completa puede generar muchos JOINs y afectar el rendimiento en consultas de lectura masiva. A veces se desnormaliza intencionalmente por rendimiento:

  • Reportes y analytics: tablas desnormalizadas (data warehouses)
  • Caché de contadores: guardar el total de vistas en la tabla del artículo en lugar de contar cada vez
  • Datos históricos: guardar el precio al momento de la venta aunque el precio actual cambie

"Normalizar una base de datos es como ordenar tu código — no es opcional si quieres que el sistema sobreviva al crecimiento."