Normalización de Bases de Datos

1NF 2NF 3NF con un ejemplo simple

🌟 ¿Qué es normalizar?

  • Limpiar el diseño de las tablas para evitar duplicaciones y anomalías (insertar, actualizar, borrar).

  • Lo haremos paso a paso: 1NF → 2NF → 3NF.

🧩 Caso de ejemplo (sin normalizar)

| id_est | nombre_est  | cursos                  | créditos | profs         |
| ------ | ----------- | ----------------------- | -------- | ------------- |
|      1 | Ana Ruiz    | Cálculo I, Probabilidad |     4, 3 | López, García |
|      2 | Carlos León | Programación            |        2 | Pérez         |

Tabla: Inscripciones (todo metido en una sola tabla)

🧩 Caso de ejemplo (sin normalizar)

| id_est | nombre_est  | cursos                  | créditos | profs       
| ------ | ----------- | ----------------------- | -------- | ------------- |
|      1 | Ana Ruiz    | Cálculo I, Probabilidad |     4, 3 | López, García |
|      2 | Carlos León | Programación            |        2 | Pérez         |

👀 Problemas:

Listas en una celda (cursos, créditos, profs).

✅ Primera Forma Normal (1NF)

Cada celda debe tener un solo valor.

Nada de columnas repetidas ni listas separadas por comas.

🧩 Caso de ejemplo (sin normalizar)

| id_est | nombre_est  | cursos                  | créditos | profs         |
| ------ | ----------- | ----------------------- | -------- | ------------- |
|      1 | Ana Ruiz    | Cálculo I, Probabilidad |     4, 3 | López, García |
|      2 | Carlos León | Programación            |        2 | Pérez         |

🧩 Caso de ejemplo (sin normalizar)

| id_est | nombre_est  | curso        | credito | prof   |
| ------ | ----------- | ------------ | ------- | ------ |
|      1 | Ana Ruiz    | Cálculo I    |       4 | López  |
|      1 | Ana Ruiz    | Probabilidad |       3 | García |
|      2 | Carlos León | Programación |       2 | Pérez  |

🧩 Caso de ejemplo (sin normalizar)

| id_est | nombre_est  | curso        | credito | prof   |
| ------ | ----------- | ------------ | ------- | ------ |
|      1 | Ana Ruiz    | Cálculo I    |       4 | López  |
|      1 | Ana Ruiz    | Probabilidad |       3 | García |
|      2 | Carlos León | Programación |       2 | Pérez  |

Solución: “descomponer” en filas (una por curso inscrito).

🧩 Caso de ejemplo (sin normalizar)

| id_est | nombre_est  | curso        | credito | prof   |
| ------ | ----------- | ------------ | ------- | ------ |
|      1 | Ana Ruiz    | Cálculo I    |       4 | López  |
|      1 | Ana Ruiz    | Probabilidad |       3 | García |
|      2 | Carlos León | Programación |       2 | Pérez  |

✨ Mejor: ya no hay listas.

🧩 Caso de ejemplo (sin normalizar)

| id_est | nombre_est  | curso        | credito | prof   |
| ------ | ----------- | ------------ | ------- | ------ |
|      1 | Ana Ruiz    | Cálculo I    |       4 | López  |
|      1 | Ana Ruiz    | Probabilidad |       3 | García |
|      2 | Carlos León | Programación |       2 | Pérez  |

⚠️ Aún hay repetición (nombre_est en varias filas).

🧩 Modelo relacional original

erDiagram
  ESTUDIANTE {
    INT id_est
    STRING nombre
    STRING apellido
    INT email
    CHAR fecha_registro
  }
  CURSO {
    STRING id_curso
    STRING nombre
    INT precio
    CHAR etiquetas
    CHAR profesores
  }
  INSCRIPCION {
    INT id_est
    DOUBLE precio
    DATE fecha
  }

  ESTUDIANTE ||--o{ INSCRIPCION : inscribe
  CURSO ||--o{ INSCRIPCION : recibe

🧩 etiquetas es multivaluado

| id_curso | nombre        | precio | etiquetas             |
|----------|---------------|--------|-----------------------|
| CUR001   | SQL Básico    | 300000 | Bases, Datos, Query   |
| CUR002   | Java Inicial  | 400000 | Programación, Objetos |
| CUR003   | Python Pro    | 500000 | Datos, Ciencia        |

→ violación de 1NF (una celda = un solo valor).

💡 Quitamos etiquetas de CURSO, pero aún no la hemos representado en otra tabla.

erDiagram
  ESTUDIANTE {
    INT id_est
    STRING nombre
    STRING apellido
    INT email
    CHAR fecha_registro
  }
  CURSO {
    STRING id_curso
    STRING nombre
    INT precio
    CHAR profesores
  }
  INSCRIPCION {
    INT id_est
    DOUBLE precio
    DATE fecha
  }

  ESTUDIANTE ||--o{ INSCRIPCION : inscribe
  CURSO ||--o{ INSCRIPCION : recibe

erDiagram
  ESTUDIANTE {
    INT id_est
    STRING nombre
    STRING apellido
    INT email
    CHAR fecha_registro
  }

  CURSO {
    STRING id_curso
    STRING nombre
    INT precio
    CHAR profesores
  }

  INSCRIPCION {
    INT id_est
    DOUBLE precio
    DATE fecha
  }

  ETIQUETA {
    STRING id_etiqueta
    STRING nombre
  }

  CURSO_ETIQUETA {
    STRING id_curso
    STRING id_etiqueta
  }

  ESTUDIANTE ||--o{ INSCRIPCION : inscribe
  CURSO ||--o{ INSCRIPCION : recibe

erDiagram
  ESTUDIANTE {
    INT id_est
    STRING nombre
    STRING apellido
    INT email
    CHAR fecha_registro
  }

  CURSO {
    STRING id_curso
    STRING nombre
    INT precio
    CHAR profesores    
  }

  INSCRIPCION {
    INT id_est
    DOUBLE precio
    DATE fecha
  }

  ETIQUETA {
    STRING id_etiqueta
    STRING nombre
  }

  CURSO_ETIQUETA {
    STRING id_curso
    STRING id_etiqueta
  }

  ESTUDIANTE ||--o{ INSCRIPCION : inscribe
  CURSO ||--o{ INSCRIPCION : recibe
  CURSO ||--o{ CURSO_ETIQUETA : tiene
  ETIQUETA ||--o{ CURSO_ETIQUETA : pertenece

| id_curso | nombre        | precio | etiquetas             |
|----------|---------------|--------|-----------------------|
| CUR001   | SQL Básico    | 300000 | Bases, Datos, Query   |
| CUR002   | Java Inicial  | 400000 | Programación, Objetos |
| CUR003   | Python Pro    | 500000 | Datos, Ciencia        |

| id_curso  | nombre        | precio |
|:----------|:--------------|-------:|
| CUR001    | SQL Básico    | 300000 |
| CUR002    | Java Inicial  | 400000 |
| CUR003    | Python Pro    | 500000 |

| id_curso  | id_etiqueta  |
|-----------|--------------|
| CUR001    | ETQ01        |
| CUR001    | ETQ02        |
| CUR001    | ETQ03        |
| CUR002    | ETQ04        |
| CUR002    | ETQ05        |
| CUR003    | ETQ02        |
| CUR003    | ETQ06        |

| id_etiqueta  | nombre       |
|:-------------|:-------------|
| ETQ01        | Bases        |
| ETQ02        | Datos        |
| ETQ03        | Query        |
| ETQ04        | Programación |
| ETQ05        | Objetos      |
| ETQ06        | Ciencia      |

🧠 Segunda Forma Normal (2NF)

Si la clave primaria es compuesta, ningún atributo no clave debe depender solo de una parte de la clave.

La Segunda Forma Normal (2NF) busca eliminar dependencias parciales y repeticiones innecesarias en una tabla.

En pocas palabras: cada columna debe describir únicamente al objeto principal de la tabla, y no depender de otras columnas distintas a su identificador.

Cuando una tabla mezcla información de diferentes entidades (por ejemplo, cursos y profesores), aparecen redundancias y anomalías de actualización.

🧩 Ejemplo

Supongamos que tenemos la siguiente estructura:

CURSO {
  STRING id_curso
  STRING nombre
  INT precio
  CHAR profesores
}

🧩 Ejemplo

Y los datos lucen así:

| id_curso  | nombre         | precio | profesores  |
|-----------|----------------|--------|-------------|
| C01       | SQL Básico     | 300000 | López       |
| C02       | SQL Intermedio | 400000 | López       |
| C03       | Python Pro     | 500000 | García      |
💬 ¿Qué problema hay?

A simple vista, el profesor “López” aparece varias veces.

Información del profesor está repetida.

Si el profesor cambia su nombre o apellido, deberíamos modificarlo en todas las filas. Eso genera redundancia y posibles inconsistencias.

⚠️ ¿Por qué no está en 2NF?

Aunque id_curso identifica bien cada curso, la columna profesores no describe directamente al curso, sino a otra entidad: el profesor que lo dicta.

Por tanto, esta tabla viola la 2NF, porque contiene información que depende de algo externo al curso.

🧱 Normalización a Segunda Forma Normal (2NF)

Para cumplir la 2NF, separamos la información en dos tablas:

📘 CURSO

| id_curso  | nombre         | precio  |
|-----------|----------------|---------|
| C01       | SQL Básico     | 300000  |
| C02       | SQL Intermedio | 400000  |
| C03       | Python Pro     | 500000  |

👨‍🏫 PROFESOR

| id_prof  | nombre_prof |
|----------|-------------|
| P01      | López       |
| P02      | García      |

🧾 IMPARTE

| id_prof  | id_curso |
|----------|----------|
| P01      | C01      |
| P01      | C02      |
| P02      | C03      |

✅ Resultado (ya en 2NF)

Ahora:

  • Cada tabla representa una sola entidad (curso o profesor).
  • No hay duplicación de nombres de profesores.
  • Si cambia un profesor, se actualiza solo una vez.
  • Los datos son más coherentes, limpios y fáciles de mantener.

✨ ¡La base ya cumple con la Segunda Forma Normal (2NF)!

🧼 Tercera Forma Normal (3NF)

Ningún atributo no clave debe depender de otro atributo no clave (evitar dependencias transitivas).

💼 Ejemplo 1 — Facturas con balance

| id_factura  | total  | pagos  | balance  | ganancias  |                                             |-------------|--------|--------|----------|------------|
| F001        | 1000   | 700    | 300      | 200        |
| F002        | 800    | 500    | 300      | 150        |
| F003        | 1200   | 1200   | 0        | 350        |

⚠️ ¿Qué problema hay?

| id_factura  | total  | pagos  | balance  | ganancias  |                                             |-------------|--------|--------|----------|------------|
| F001        | 1000   | 700    | 300      | 200        |
| F002        | 800    | 500    | 300      | 150        |
| F003        | 1200   | 1200   | 0        | 350        |

\(\text{balance} = \text{total} - \text{pagos}\)

Solución (en 3NF)

| id_factura  | total  | pagos  | ganancias  |
|-------------|--------|--------|------------|
| F001        | 1000   | 700    | 200        |
| F002        | 800    | 500    | 150        |
| F003        | 1200   | 1200   | 350        |

Así la tabla cumple la 3NF ✨ -

Guardamos solo los datos base, no los derivados:

👥 Ejemplo 2 — Nombres y apellidos

| id_cliente  | nombre_completo    | nombre  | apellido  |                                            |-------------|--------------------|---------|-----------|
| C001        | Ana Ruiz           | Ana     | Ruiz      |
| C002        | Carlos León        | Carlos  | León      |

✅ En 3NF (forma correcta)

Dejamos solo los atributos atómicos:

| id_cliente | nombre | apellido |
|-------------|---------|-----------|
| C001        | Ana     | Ruiz      |
| C002        | Carlos  | León      |
| C003        | Juan    | Pérez García |

Mini checklist de normalización

  • 1NF: ¿Hay listas o campos multivalor en una celda? → romper en filas.

  • 2NF: Si la PK es compuesta, ¿hay atributos que dependan de una sola parte? → separar.

  • 3NF: ¿Algún atributo no clave depende de otro no clave? → factorizar. -

Mensaje final

Normalizar es separar lo que depende de cosas diferentes, Para que los datos sean coherentes, fáciles de mantener y consultar.