1.- Conceptos de Normalización de Bases de Datos
La normalización de bases de datos es el proceso sistemático de estructurar tablas y relaciones en un sistema de gestión de bases de datos relacionales (RDBMS) con el fin de minimizar la redundancia de datos, optimizar el espacio de almacenamiento y garantizar la integridad de la información.
2.- Objetivos Principales
Reducción de la redundancia: Almacenar cada dato en su única ubicación lógica para evitar duplicidades innecesarias.
Garantía de integridad referencial y de dominio: Asegurar que las relaciones lógicas entre entidades se mantengan consistentes.
Prevención de anomalías operacionales:
Anomalías de inserción: Dificultad o imposibilidad de registrar datos independientes sin la presencia previa de otros datos.
Anomalías de actualización: Inconsistencias generadas al modificar un valor en un registro pero omitirlo en duplicados dependientes.
Anomalías de borrado: Pérdida colateral e involuntaria de información válida al eliminar un registro principal.
3.- Tabla Resumen de Procedimientos (Formas Normales)
| Forma Normal | Requisito Previo | Regla de Validación y Procedimiento | Problema / Anomalía que Resuelve |
| 1ª Forma Normal (1FN) | Estructura tabular básica | Todos los atributos deben contener valores atómicos (indivisibles). Se deben eliminar los grupos repetitivos o colecciones anidadas y asegurar que cada fila esté identificada por una clave primaria única. | Presencia de listas, arrays o celdas con múltiples valores en una sola columna. |
| 2ª Forma Normal (2FN) | Estar en 1FN | La tabla debe cumplir la 1FN y todos los atributos que no forman parte de la clave primaria deben depender funcionalmente de la clave primaria completa (se eliminan las dependencias parciales en claves compuestas). | Dependencia parcial en claves compuestas (atributos que solo dependen de una parte de la clave). |
| 3ª Forma Normal (3FN) | Estar en 2FN | La tabla debe cumplir la 2FN y eliminar las dependencias transitivas. Ningún atributo no clave debe depender de otro atributo que tampoco sea clave ($A \to B \to C$). | Dependencia transitiva y redundancia de datos derivados entre columnas no clave. |
| Forma Normal de Boyce-Codd (FNBC) | Estar en 3FN | Es una versión más estricta de la 3FN. Para cualquier dependencia funcional no trivial $X \to Y$, el determinante $X$ debe ser obligatoriamente una superclave. | Solapamiento de múltiples claves candidatas con dependencias cruzadas parciales. |
| 4ª Forma Normal (4FN) | Estar en FNBC | La tabla no debe contener dependencias multivaloradas no triviales. Si un registro asocia de manera independiente múltiples valores a dos o más variables, deben aislarse en tablas separadas. | Multiplicación combinatoria innecesaria de filas por dependencias multivalor independientes. |
| 5ª Forma Normal (5FN) | Estar en 4FN | Trata las dependencias de unión (join dependencies). Garantiza que la tabla se pueda descomponer en tablas menores y volver a reconstruir mediante una operación de unión sin pérdida de información (lossless join). | Pérdida de restricciones estructurales complejas al fragmentar relaciones n-arias. |
Guía Definitiva de Normalización de Bases de Datos Relacionales: De 1NF a 3NF con Casos Prácticos
Por: TCC Developer Team | Optimización de Sistemas y Diseño Relacional
VideoLa normalización de bases de datos es un proceso formal de diseño estructurado que busca minimizar la redundancia de datos y evitar anomalías en las operaciones de inserción, actualización y borrado (CRUD). En esta guía técnica de nivel avanzado, analizaremos paso a paso cómo transformar un esquema completamente desnormalizado aplicando rigurosamente la Primera (1NF), Segunda (2NF) y Tercera Forma Normal (3NF), utilizando un escenario práctico de gestión académica de pilotos y tecnologías.
Objetivo de la normalización: Garantizar la integridad referencial, optimizar el rendimiento de almacenamiento y prevenir la inconsistencia de datos en entornos de producción transaccionales (OLTP).
1. El Problema: Base de Datos No Normalizada (Estado Inicial)
Imaginemos una tabla inicial en una base de datos MySQL que almacena información sobre los pilotos, los lenguajes de programación que estudian, sus instructores asignados y las aulas correspondientes. En un diseño inicial defectuoso (o en la importación cruda de hojas de cálculo), nos encontramos con atributos multivaluados (múltiples valores en un mismo campo o fila) y una masiva redundancia de datos.
A continuación, proporcionamos el script SQL completo para crear la tabla desnormalizada e insertar 25 registros iniciales que simulan este escenario caótico. Puedes copiar y pegar este código directamente en phpMyAdmin.
Script SQL: Creación e Inserción de 25 Registros Desnormalizados
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | -- Eliminar la tabla si existe para pruebas limpias DROP TABLE IF EXISTS `cursos_pilotos_denormalizada`; -- Crear la tabla denormalizada CREATE TABLE `cursos_pilotos_denormalizada` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `piloto_id` INT NOT NULL, `piloto_nombre` VARCHAR(100) NOT NULL, `lenguajes` VARCHAR(255) NOT NULL, `instructores` VARCHAR(255) NOT NULL, `aulas` VARCHAR(50) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Inserción de 25 registros desnormalizados INSERT INTO `cursos_pilotos_denormalizada` (`piloto_id`, `piloto_nombre`, `lenguajes`, `instructores`, `aulas`) VALUES (1, 'Max Verstappen', 'Java, Python', 'García, Fernán', 'Aula A1, Aula B2'), (1, 'Max Verstappen', 'Java, Python', 'García, Fernán', 'Aula A1, Aula B2'), (2, 'Charles Leclerc', 'Java', 'García', 'Aula A1'), (2, 'Charles Leclerc', 'Java', 'García', 'Aula A1'), (3, 'Lewis Hamilton', 'JavaScript, Java', 'López, García', 'Aula C3, Aula A1'), (3, 'Lewis Hamilton', 'JavaScript, Java', 'López, García', 'Aula C3, Aula A1'), (3, 'Lewis Hamilton', 'JavaScript, Java', 'López, García', 'Aula C3, Aula A1'), (4, 'Franco Colapinto', 'Python', 'Fernán', 'Aula B2'), (4, 'Franco Colapinto', 'Python', 'Fernán', 'Aula B2'), (5, 'Carlos Sainz', 'C++, Python', 'Martínez, Fernán', 'Aula D4, Aula B2'), (5, 'Carlos Sainz', 'C++, Python', 'Martínez, Fernán', 'Aula D4, Aula B2'), (6, 'Lando Norris', 'JavaScript, TypeScript', 'López, Gómez', 'Aula C3, Aula C5'), (6, 'Lando Norris', 'JavaScript, TypeScript', 'López, Gómez', 'Aula C3, Aula C5'), (7, 'Sergio Pérez', 'PHP, MySQL', 'Pérez, Ruiz', 'Aula E1, Aula E2'), (7, 'Sergio Pérez', 'PHP, MySQL', 'Pérez, Ruiz', 'Aula E1, Aula E2'), (8, 'Fernando Alonso', 'Java, C#', 'García, Torres', 'Aula A1, Aula F1'), (8, 'Fernando Alonso', 'Java, C#', 'García, Torres', 'Aula A1, Aula F1'), (8, 'Fernando Alonso', 'Java, C#', 'García, Torres', 'Aula A1, Aula F1'), (9, 'Oscar Piastri', 'Python, Go', 'Fernán, Castro', 'Aula B2, Aula G1'), (9, 'Oscar Piastri', 'Python, Go', 'Fernán, Castro', 'Aula B2, Aula G1'), (10, 'George Russell', 'JavaScript, Python', 'López, Fernán', 'Aula C3, Aula B2'), (10, 'George Russell', 'JavaScript, Python', 'López, Fernán', 'Aula C3, Aula B2'), (10, 'George Russell', 'JavaScript, Python', 'López, Fernán', 'Aula C3, Aula B2'), (10, 'George Russell', 'JavaScript, Python', 'López, Fernán', 'Aula C3, Aula B2'), (10, 'George Russell', 'JavaScript, Python', 'López, Fernán', 'Aula C3, Aula B2'); |
Análisis del problema: Como se puede observar, este diseño viola principios fundamentales de bases de datos relacionales. Los campos contienen listas separadas por comas, los nombres de los instructores y aulas se repiten múltiples veces, y cualquier modificación requeriría actualizar múltiples filas, propiciando anomalías e inconsistencias.
2. Primera Forma Normal (1NF): Atomicidad de los Datos
La Primera Forma Normal (1NF) establece que los dominios de los atributos deben ser atómicos; es decir, cada celda de la tabla debe contener un único valor indivisible y no deben existir grupos repetitivos o listas de valores dentro de un mismo campo.
Para cumplir con la 1NF, descomponemos los atributos multivaluados de modo que cada combinación de piloto, lenguaje, instructor y aula ocupe una tupla (fila) independiente y totalmente atómica.
Script SQL: Implementación de la 1NF
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | -- Crear tabla normalizada en 1NF (valores atómicos) DROP TABLE IF EXISTS `pilotos_1nf`; CREATE TABLE `pilotos_1nf` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `piloto_id` INT NOT NULL, `piloto_nombre` VARCHAR(100) NOT NULL, `lenguaje` VARCHAR(50) NOT NULL, `instructor` VARCHAR(100) NOT NULL, `aula` VARCHAR(50) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Inserción de datos atómicos correspondientes INSERT INTO `pilotos_1nf` (`piloto_id`, `piloto_nombre`, `lenguaje`, `instructor`, `aula`) VALUES (1, 'Max Verstappen', 'Java', 'García', 'Aula A1'), (1, 'Max Verstappen', 'Python', 'Fernán', 'Aula B2'), (2, 'Charles Leclerc', 'Java', 'García', 'Aula A1'), (3, 'Lewis Hamilton', 'JavaScript', 'López', 'Aula C3'), (3, 'Lewis Hamilton', 'Java', 'García', 'Aula A1'), (4, 'Franco Colapinto', 'Python', 'Fernán', 'Aula B2'), (5, 'Carlos Sainz', 'C++', 'Martínez', 'Aula D4'), (5, 'Carlos Sainz', 'Python', 'Fernán', 'Aula B2'), (6, 'Lando Norris', 'JavaScript', 'López', 'Aula C3'), (6, 'Lando Norris', 'TypeScript', 'Gómez', 'Aula C5'), (7, 'Sergio Pérez', 'PHP', 'Pérez', 'Aula E1'), (7, 'Sergio Pérez', 'MySQL', 'Ruiz', 'Aula E2'), (8, 'Fernando Alonso', 'Java', 'García', 'Aula A1'), (8, 'Fernando Alonso', 'C#', 'Torres', 'Aula F1'), (9, 'Oscar Piastri', 'Python', 'Fernán', 'Aula B2'), (9, 'Oscar Piastri', 'Go', 'Castro', 'Aula G1'), (10, 'George Russell', 'JavaScript', 'López', 'Aula C3'), (10, 'George Russell', 'Python', 'Fernán', 'Aula B2'); |
3. Segunda Forma Normal (2NF): Eliminación de Dependencias Parciales
La Segunda Forma Normal (2NF) exige que la tabla esté en 1NF y que todos los atributos que no forman parte de la clave primaria dependan completamente de toda la clave primaria (se eliminan las dependencias parciales).
Para resolver esto, se secciona la estructura en dos tablas principales: una entidad independiente para los Pilotos y una tabla intermedia que relacione los pilotos con sus lenguajes.
Script SQL: Implementación de la 2NF
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | DROP TABLE IF EXISTS `piloto_lenguaje_2nf`; DROP TABLE IF EXISTS `pilotos_2nf`; CREATE TABLE `pilotos_2nf` ( `id_piloto` INT PRIMARY KEY, `nombre` VARCHAR(100) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `pilotos_2nf` (`id_piloto`, `nombre`) VALUES (1, 'Max Verstappen'), (2, 'Charles Leclerc'), (3, 'Lewis Hamilton'), (4, 'Franco Colapinto'), (5, 'Carlos Sainz'), (6, 'Lando Norris'), (7, 'Sergio Pérez'), (8, 'Fernando Alonso'), (9, 'Oscar Piastri'), (10, 'George Russell'); CREATE TABLE `piloto_lenguaje_2nf` ( `id_piloto` INT NOT NULL, `lenguaje` VARCHAR(50) NOT NULL, `instructor` VARCHAR(100) NOT NULL, `aula` VARCHAR(50) NOT NULL, PRIMARY KEY (`id_piloto`, `lenguaje`), CONSTRAINT `fk_piloto_2nf` FOREIGN KEY (`id_piloto`) REFERENCES `pilotos_2nf` (`id_piloto`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `piloto_lenguaje_2nf` (`id_piloto`, `lenguaje`, `instructor`, `aula`) VALUES (1, 'Java', 'García', 'Aula A1'), (1, 'Python', 'Fernán', 'Aula B2'), (2, 'Java', 'García', 'Aula A1'), (3, 'JavaScript', 'López', 'Aula C3'), (3, 'Java', 'García', 'Aula A1'), (4, 'Python', 'Fernán', 'Aula B2'), (5, 'C++', 'Martínez', 'Aula D4'), (5, 'Python', 'Fernán', 'Aula B2'), (6, 'JavaScript', 'López', 'Aula C3'), (6, 'TypeScript', 'Gómez', 'Aula C5'), (7, 'PHP', 'Pérez', 'Aula E1'), (7, 'MySQL', 'Ruiz', 'Aula E2'), (8, 'Java', 'García', 'Aula A1'), (8, 'C#', 'Torres', 'Aula F1'), (9, 'Python', 'Fernán', 'Aula B2'), (9, 'Go', 'Castro', 'Aula G1'), (10, 'JavaScript', 'López', 'Aula C3'), (10, 'Python', 'Fernán', 'Aula B2'); |
4. Tercera Forma Normal (3NF): Eliminación de Dependencias Transitivas
La Tercera Forma Normal (3NF) requiere que la tabla esté en 2NF y que no existan dependencias transitivas entre los atributos que no forman parte de la clave.
Aislamos los lenguajes en su propia entidad con instructores y aulas normalizadas, manteniendo una tabla relacional limpia.
Script SQL: Implementación de la 3NF
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | DROP TABLE IF EXISTS `piloto_lenguaje_3nf`; DROP TABLE IF EXISTS `lenguajes_3nf`; DROP TABLE IF EXISTS `pilotos_3nf`; CREATE TABLE `pilotos_3nf` ( `id_piloto` INT PRIMARY KEY, `nombre` VARCHAR(100) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `pilotos_3nf` (`id_piloto`, `nombre`) VALUES (1, 'Max Verstappen'), (2, 'Charles Leclerc'), (3, 'Lewis Hamilton'), (4, 'Franco Colapinto'), (5, 'Carlos Sainz'), (6, 'Lando Norris'), (7, 'Sergio Pérez'), (8, 'Fernando Alonso'), (9, 'Oscar Piastri'), (10, 'George Russell'); CREATE TABLE `lenguajes_3nf` ( `id_lenguaje` INT AUTO_INCREMENT PRIMARY KEY, `nombre_lenguaje` VARCHAR(50) NOT NULL UNIQUE, `instructor` VARCHAR(100) NOT NULL, `aula` VARCHAR(50) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `lenguajes_3nf` (`nombre_lenguaje`, `instructor`, `aula`) VALUES ('Java', 'García', 'Aula A1'), ('Python', 'Fernán', 'Aula B2'), ('JavaScript', 'López', 'Aula C3'), ('C++', 'Martínez', 'Aula D4'), ('TypeScript', 'Gómez', 'Aula C5'), ('PHP', 'Pérez', 'Aula E1'), ('MySQL', 'Ruiz', 'Aula E2'), ('C#', 'Torres', 'Aula F1'), ('Go', 'Castro', 'Aula G1'); CREATE TABLE `piloto_lenguaje_3nf` ( `id_piloto` INT NOT NULL, `id_lenguaje` INT NOT NULL, PRIMARY KEY (`id_piloto`, `id_lenguaje`), CONSTRAINT `fk_p3nf_piloto` FOREIGN KEY (`id_piloto`) REFERENCES `pilotos_3nf` (`id_piloto`) ON DELETE CASCADE, CONSTRAINT `fk_p3nf_lenguaje` FOREIGN KEY (`id_lenguaje`) REFERENCES `lenguajes_3nf` (`id_lenguaje`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `piloto_lenguaje_3nf` (`id_piloto`, `id_lenguaje`) VALUES (1, 1), (1, 2), (2, 1), (3, 3), (3, 1), (4, 2), (5, 4), (5, 2), (6, 3), (6, 5), (7, 6), (7, 7), (8, 1), (8, 8), (9, 2), (9, 9), (10, 3), (10, 2); |
5. Arquitectura de Alto Nivel: Normalización Avanzada
En entornos empresariales, instructores y aulas se separan en tablas independientes con claves foráneas dedicadas para garantizar escalabilidad y evitar redundancias de cadenas de texto.
Script SQL: Esquema Enterprise Normalizado
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | DROP TABLE IF EXISTS `curso_asignacion`; DROP TABLE IF EXISTS `aulas`; DROP TABLE IF EXISTS `instructores`; DROP TABLE IF EXISTS `cursos`; DROP TABLE IF EXISTS `pilotos_enterprise`; CREATE TABLE `pilotos_enterprise` ( `id_piloto` INT AUTO_INCREMENT PRIMARY KEY, `nombre` VARCHAR(100) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `instructores` ( `id_instructor` INT AUTO_INCREMENT PRIMARY KEY, `nombre_instructor` VARCHAR(100) NOT NULL, `especialidad` VARCHAR(100) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `aulas` ( `id_aula` INT AUTO_INCREMENT PRIMARY KEY, `codigo_aula` VARCHAR(50) NOT NULL UNIQUE, `capacidad` INT DEFAULT 30 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `cursos` ( `id_curso` INT AUTO_INCREMENT PRIMARY KEY, `nombre_curso` VARCHAR(50) NOT NULL, `id_instructor` INT NOT NULL, `id_aula` INT NOT NULL, CONSTRAINT `fk_curso_instructor` FOREIGN KEY (`id_instructor`) REFERENCES `instructores` (`id_instructor`), CONSTRAINT `fk_curso_aula` FOREIGN KEY (`id_aula`) REFERENCES `aulas` (`id_aula`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `curso_asignacion` ( `id_piloto` INT NOT NULL, `id_curso` INT NOT NULL, PRIMARY KEY (`id_piloto`, `id_curso`), CONSTRAINT `fk_asig_piloto` FOREIGN KEY (`id_piloto`) REFERENCES `pilotos_enterprise` (`id_piloto`) ON DELETE CASCADE, CONSTRAINT `fk_asig_curso` FOREIGN KEY (`id_curso`) REFERENCES `cursos` (`id_curso`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; |
