Bases de datos

Normalización de bases de datos: por qué y cómo se normaliza

Normalizar una base de datos es repartir sus datos en varias tablas unidas por claves, para que cada hecho se escriba una sola vez. Eso elimina tres fallas: datos que se contradicen al corregirlos, datos que se pierden al borrar y registros imposibles de crear sin inventar valores. Este tutorial lo hace paso a paso, con código que usted puede ejecutar.

Por Jhon Mosquera24 oct 202616 min de lectura

Última actualización: 2026-10-24

Contenido de esta página

Historia real. En octubre de 2011, el Congreso de Colombia expidió el Estatuto del Consumidor, la ley que protege a quien compra [1]. Uno de sus artículos, escrito para el comercio, describe sin proponérselo un problema de datos. Dice que el consumidor «solo estará obligado a pagar el precio anunciado» [1]. Y si aparecen dos o más precios, paga «el precio más bajo de los que aparezcan indicados» [1].

La escena es conocida: la etiqueta de la góndola dice un precio y la caja registra otro. El mismo dato, el precio de un producto, quedó escrito en dos lugares, y uno cambió sin el otro. La ley no pregunta cuál de los dos se equivocó. Resuelve a favor del comprador, sin perjuicio de las sanciones que puedan caerle a la tienda [1].

Dentro de una base de datos no hay ley que arbitre. Suponga una tabla que escribe la dirección de un cliente en cada fila de sus pedidos. Basta con corregirla en una fila y olvidar otra para tener dos versiones del mismo dato, y ningún artículo dice cuál es la buena.

La normalización existe para que ese pleito no pueda empezar.

Casillero de correspondencia azul con sobres en sus compartimentos y un montón de sobres sin clasificar.

¿Qué es la normalización de bases de datos?

La normalización de bases de datos es un procedimiento de diseño. Toma una tabla donde los mismos datos se repiten fila tras fila y la divide en tablas más pequeñas, una por cada tema: clientes, productos, pedidos. Las tablas quedan conectadas por claves, que son las columnas que identifican cada fila sin confusión posible. Al final, cambiar un dato exige tocar un solo lugar.

Unos apuntes de la Universidad de Granada, en España, lo presentan como un refinamiento de un diseño que puede tener defectos, como los que provocan redundancia de datos [2]. Relación es el nombre técnico de una tabla. Según esos apuntes, normalizar da como resultado «un nuevo conjunto de relaciones ampliado respecto al original» [2]. La teoría llama formas normales a los niveles de este procedimiento y tiene una definición formal para cada uno; este tutorial se queda con el porqué y el cómo.

Qué significan redundancia y anomalía está definido en el glosario de fundamentos de bases de datos. Aquí se ven fallar en una tabla de ejemplo y se reparan con código.

¿Para qué sirve normalizar una base de datos?

Normalizar sirve para que la base no se contradiga, no pierda datos al borrar y acepte datos nuevos sin trucos. Edgar F. Codd propuso el modelo de tablas relacionadas cuando investigaba en IBM, la empresa estadounidense de computadores [8]. En un informe de 1971 le fijó objetivos a la normalización [3]. El primero era «liberar la colección de relaciones de las dependencias indeseables de inserción, actualización y borrado» [3]. Esas dependencias indeseables son las anomalías que esta página demuestra. Las citas en inglés de esta página son traducción propia. Otro era reducir la necesidad de reestructurar las tablas cuando llegan tipos de datos nuevos, para alargar la vida de los programas [3].

Doce años después, William Kent lo resumió en la revista de la ACM, la asociación internacional de profesionales de la computación. Escribió que las reglas de normalización «están diseñadas para prevenir anomalías de actualización e inconsistencias en los datos» [4].

¿Normalización de datos es lo mismo que normalización de bases de datos?

No siempre. En bases de datos, normalizar es organizar tablas para que cada hecho quede guardado una sola vez. En el aprendizaje automático, la rama de la inteligencia artificial que aprende patrones a partir de ejemplos, la palabra nombra otra cosa: cambiar la escala de los números.

La biblioteca scikit-learn, una caja de herramientas de aprendizaje automático para el lenguaje Python, llama normalización a ajustar cada muestra para que su tamaño, medido como vector, valga 1 [5]. Aparte ofrece el reescalado de cada variable a un rango, a menudo entre cero y uno [5]. Ese segundo sentido pertenece al aprendizaje automático, y esta página no lo trata.

Ejemplo de normalización de base de datos: los pedidos de una tienda de café

El ejemplo de este tutorial es una tienda de café de Bucaramanga que vende por WhatsApp, la aplicación de mensajes, y entrega a domicilio. La tienda es inventada y sus datos también. Lleva los pedidos en una sola tabla, como si fuera una hoja de cálculo: una fila por cada producto de cada pedido.

pedidoproductofechaclientedireccionpreciocantidad
1001Café molido 500 g2026-10-05Luz Mery RuedaCl 45 # 27-10320002
1001Filtro de tela2026-10-05Luz Mery RuedaCl 45 # 27-1090001
1002Café molido 500 g2026-10-06Andrés PintoCra 9 # 12-40320001
1003Café en grano 500 g2026-10-08Luz Mery RuedaCl 45 # 27-10350001
1004Molinillo manual2026-10-09Sofía GómezCl 33 # 20-15850001

La tabla parece ordenada, y nada en ella está mal escrito. El problema está en su forma. Luz Mery y su dirección aparecen tres veces, y el café molido con su precio aparece dos.

Para seguir el tutorial basta SQLite, un motor de bases de datos gratuito que guarda todo en un solo archivo. Corrí cada bloque en su versión 3.53.1, publicada en mayo de 2026 [6]. Las órdenes están escritas en SQL, el lenguaje estándar para crear y consultar tablas.

-- La tabla de pedidos tal como la lleva la tienda: una fila por producto pedido
CREATE TABLE pedidos_planos (
  pedido    INTEGER NOT NULL,  -- en SQLite, esta clave primaria no prohíbe vacíos:
  producto  TEXT    NOT NULL,  -- por eso NOT NULL va escrito aparte
  fecha     TEXT    NOT NULL,
  cliente   TEXT    NOT NULL,
  direccion TEXT    NOT NULL,
  precio    INTEGER NOT NULL,
  cantidad  INTEGER NOT NULL,
  PRIMARY KEY (pedido, producto)
);

INSERT INTO pedidos_planos VALUES
  (1001, 'Café molido 500 g',   '2026-10-05', 'Luz Mery Rueda', 'Cl 45 # 27-10', 32000, 2),
  (1001, 'Filtro de tela',      '2026-10-05', 'Luz Mery Rueda', 'Cl 45 # 27-10',  9000, 1),
  (1002, 'Café molido 500 g',   '2026-10-06', 'Andrés Pinto',   'Cra 9 # 12-40', 32000, 1),
  (1003, 'Café en grano 500 g', '2026-10-08', 'Luz Mery Rueda', 'Cl 45 # 27-10', 35000, 1),
  (1004, 'Molinillo manual',    '2026-10-09', 'Sofía Gómez',    'Cl 33 # 20-15', 85000, 1);

La clave primaria, es decir, lo que identifica cada fila, es la pareja de pedido y producto. Ningún pedido lleva dos veces el mismo producto, así que esa combinación no se repite. El comentario del código tiene una razón. La norma de SQL exige que una clave primaria nunca quede vacía. La documentación de SQLite reconoce que este motor lo permite en la mayoría de los casos, por un error de algunas versiones antiguas [7]. Lo conservó para no romper aplicaciones viejas, así que la regla hay que escribirla [7].

Las tres anomalías de una tabla sin normalizar, con SQL

Una tabla sin normalizar falla de tres maneras, que la teoría llama anomalías. Al actualizar, un cambio hecho en una fila y no en las demás deja datos que se contradicen. Al borrar, eliminar una fila se lleva datos que nadie quería perder. Al insertar, hay datos que no se pueden guardar sin inventar otros. Las tres se pueden provocar a propósito, y cada prueba de abajo parte de la tabla original.

Anomalía de actualización: la misma clienta con dos direcciones

Luz Mery se muda y avisa por WhatsApp. Quien atiende abre su último pedido y corrige la dirección ahí. Es lo natural, y es el error.

-- Luz Mery se mudó. Quien atiende corrige la dirección solo en su último pedido
UPDATE pedidos_planos
SET direccion = 'Cra 33 # 48-02'
WHERE pedido = 1003;

-- ¿Dónde vive Luz Mery?
SELECT DISTINCT cliente, direccion
FROM pedidos_planos
WHERE cliente = 'Luz Mery Rueda';
-- Luz Mery Rueda|Cl 45 # 27-10
-- Luz Mery Rueda|Cra 33 # 48-02

La orden UPDATE (actualizar) cambió una fila, y la consulta devuelve dos direcciones para una sola clienta. El domiciliario de su próximo pedido puede ir a cualquiera de las dos. Codd describió el mismo problema en 1971 con un proveedor que trasladaba su base de operaciones: había que corregir más de una fila [3]. Y advirtió que «el número de tuplas que hay que actualizar puede cambiar, y por lo general cambiará, con el tiempo» [3]. Tupla es otro nombre técnico de la fila.

Anomalía de borrado: cancelar un pedido borra a una clienta

Sofía cancela su único pedido, el 1004. Borrarlo parece lo correcto, porque ese pedido ya no existe.

-- Sofía cancela el pedido 1004, y se borra
DELETE FROM pedidos_planos WHERE pedido = 1004;

-- ¿Qué queda de Sofía y del molinillo?
SELECT cliente, direccion FROM pedidos_planos WHERE cliente = 'Sofía Gómez';
SELECT producto, precio FROM pedidos_planos WHERE producto = 'Molinillo manual';
-- (ninguna fila en las dos consultas)

Con el pedido se fueron Sofía y su dirección. También se fue el molinillo con su precio. La tienda sigue teniendo esa clienta y ese producto en la vitrina, pero la base ya no lo sabe. La orden DELETE (borrar) no distingue entre el pedido y los datos que viajaban con él.

Anomalía de inserción: el producto que no se puede registrar

En su informe de 1971, Codd ilustró el borrado con proveedores y piezas, y dejó la anomalía de inserción como ejercicio para el lector [3]. Aquí va resuelto con la tienda, que empieza a vender café descafeinado. Nadie lo ha pedido todavía.

-- Llega un producto nuevo que nadie ha pedido todavía
INSERT INTO pedidos_planos (producto, precio)
VALUES ('Café descafeinado 250 g', 21000);
-- Falla: NOT NULL constraint failed: pedidos_planos.pedido

-- La salida de emergencia: inventar un pedido y un cliente
INSERT INTO pedidos_planos VALUES
  (0, 'Café descafeinado 250 g', '1900-01-01', 'SIN CLIENTE', 'SIN DIRECCIÓN', 21000, 0);

SELECT COUNT(DISTINCT pedido) AS pedidos, COUNT(DISTINCT cliente) AS clientes
FROM pedidos_planos;
-- 5|4  (la tienda tiene 4 pedidos y 3 clientes de verdad)

La tabla no admite un producto sin pedido, porque el pedido es parte de su clave. La salida de emergencia es inventar un pedido 0 y un cliente de relleno. Funciona, y ensucia todo lo que se cuente después: la consulta ve cinco pedidos y cuatro clientes donde hay cuatro y tres.

Dependencia funcional, parcial y transitiva: qué columna depende de cuál

Una dependencia funcional es una regla del negocio según la cual una columna determina otra. Si se conoce el número de un pedido, se conoce su fecha, porque cada pedido tiene una sola. Las tres anomalías vienen de columnas que dependen de algo distinto de la clave completa de la tabla. Encontrar esas dependencias es encontrar por dónde cortar.

Dependencia funcional: cuando conocer una columna basta para conocer otra

Codd la definió en su informe de 1971. Una columna depende de otra si, en todo momento, cada valor de esa otra tiene asociado como máximo un valor de la primera [3]. En la tienda, cada pedido tiene una fecha y cada cliente, una dirección.

Lo de «en todo momento» importa. Que hoy ningún pedido tenga dos fechas no prueba la regla, y un solo caso contrario basta para desmentirla. Por eso las dependencias no se descubren mirando los datos: se le preguntan al negocio. Codd había advertido algo parecido en 1970, sobre las redundancias entre tablas. Si el sistema no tiene información detallada sobre el significado de cada tabla, y escribió que lo más probable es que no la tenga, no puede deducir qué redundancias hay [8].

Lo que sí se puede hacer con los datos es buscar contradicciones. En la consulta de abajo, GROUP BY (agrupar por) junta las filas de cada pedido o de cada cliente. Después, HAVING (que tenga) deja solo los grupos que rompen la regla.

-- Si el pedido determina la fecha, ningún pedido puede tener dos fechas
SELECT pedido, COUNT(DISTINCT fecha) AS fechas
FROM pedidos_planos
GROUP BY pedido
HAVING COUNT(DISTINCT fecha) > 1;
-- (ninguna fila)

-- Si el cliente determina la dirección, ningún cliente puede tener dos direcciones
SELECT cliente, COUNT(DISTINCT direccion) AS direcciones
FROM pedidos_planos
GROUP BY cliente
HAVING COUNT(DISTINCT direccion) > 1;
-- Con la tabla original: ninguna fila
-- Después del cambio de dirección de arriba: Luz Mery Rueda|2

Dependencia parcial: lo que depende de media clave

La clave de la tabla tiene dos partes, pedido y producto. La fecha y el cliente dependen solo del pedido: no cambian según el producto. El precio de lista depende solo del producto, porque el café molido vale 32.000 pesos en cualquier pedido. Eso es una dependencia parcial, y explica las repeticiones: la fecha se copia una vez por cada producto del pedido, y el precio, una vez por cada pedido del producto.

La cantidad, en cambio, depende de la clave entera. Que sean dos bolsas de café molido es un dato del pedido 1001 y del producto a la vez.

Dependencia transitiva: lo que depende de la clave a través de otra columna

La dirección no depende del pedido de forma directa. El pedido dice quién es el cliente, y el cliente dice dónde vive. Esa cadena de dos pasos es una dependencia transitiva: la dirección llega a la clave pasando por el cliente. Es la que produjo la anomalía de actualización, porque cada pedido arrastra su propia copia de la dirección.

El precio pide una pregunta más. Si es el precio de lista, depende del producto. Si es lo que se cobró en esa venta, depende del pedido y del producto, porque un descuento o un alza lo cambian sin cambiar el producto. Son dos datos distintos, igual que en la ley del comienzo, que distingue el precio anunciado del que se cobra.

ColumnaDe qué dependeTipo de dependenciaTabla a la que se muda
fechadel pedidoparcial: media clavepedidos
clientedel pedidoparcial: media clavepedidos
direcciondel cliente, que depende del pedidotransitivaclientes
precio de listadel productoparcial: media claveproductos
precio cobradodel pedido y del producto juntosde la clave completalíneas del pedido
cantidaddel pedido y del producto juntosde la clave completalíneas del pedido

Cómo normalizar una base de datos paso a paso

Normalizar una tabla es repetir una misma pregunta, columna por columna: ¿de qué depende este dato? Cada grupo de columnas que depende de algo distinto de la clave completa se muda a una tabla propia, con su propia clave. Las tablas nuevas se unen con claves foráneas, columnas que apuntan a la clave de otra tabla. Al final se comprueba que la tabla original se puede reconstruir sin perder ni ganar una fila.

  1. Nombre las cosas que la tabla describe. En la tienda son cuatro: clientes, productos, pedidos y las líneas de cada pedido.
  2. Encuentre la clave de la tabla, lo que identifica cada fila. Aquí es la pareja de pedido y producto.
  3. Pregunte de qué depende cada columna, y confirme la respuesta con el negocio, no con los datos de hoy.
  4. Saque a una tabla propia lo que depende de media clave. La fecha y el cliente van a pedidos; el nombre y el precio de lista, a productos. La cantidad y el precio cobrado se quedan en las líneas del pedido.
  5. Saque lo que depende de otra columna que no es clave. La dirección va a clientes.
  6. Dé a cada tabla nueva su clave primaria. A clientes y productos les conviene un número propio, porque dos personas pueden llamarse igual y un nombre se puede corregir.
  7. Conecte las tablas con claves foráneas, las columnas que apuntan a la clave de otra tabla. El pedido apunta a su cliente, y cada línea, a su pedido y a su producto.
  8. Reconstruya la tabla original uniendo las nuevas y compárela con la de antes. Si sobra o falta una fila, algún corte quedó mal.
  9. Repita las tres pruebas de las anomalías sobre el diseño nuevo.

El resultado son cuatro tablas, el mismo esquema de tienda que bases de datos relacionales muestra ya terminado. La diferencia es que aquí se llegó a él cortando la tabla original, no copiando un modelo.

El diseño normalizado en SQL, con claves primarias y foráneas

-- En SQLite, las claves foráneas solo se revisan si se activan en cada conexión
PRAGMA foreign_keys = ON;

CREATE TABLE clientes (
  id_cliente INTEGER PRIMARY KEY,
  nombre     TEXT NOT NULL,
  direccion  TEXT NOT NULL
);

CREATE TABLE productos (
  id_producto INTEGER PRIMARY KEY,
  nombre      TEXT NOT NULL UNIQUE,
  precio      INTEGER NOT NULL CHECK (precio > 0)  -- precio de lista de hoy
);

CREATE TABLE pedidos (
  id_pedido  INTEGER PRIMARY KEY,
  fecha      TEXT NOT NULL,
  id_cliente INTEGER NOT NULL REFERENCES clientes (id_cliente)
);

CREATE TABLE lineas_pedido (
  id_pedido      INTEGER NOT NULL REFERENCES pedidos (id_pedido) ON DELETE CASCADE,
  id_producto    INTEGER NOT NULL REFERENCES productos (id_producto),
  cantidad       INTEGER NOT NULL CHECK (cantidad > 0),
  precio_cobrado INTEGER NOT NULL,  -- el precio de esa venta: es un dato del pedido
  PRIMARY KEY (id_pedido, id_producto)
);

INSERT INTO clientes VALUES
  (1, 'Luz Mery Rueda', 'Cl 45 # 27-10'),
  (2, 'Andrés Pinto',   'Cra 9 # 12-40'),
  (3, 'Sofía Gómez',    'Cl 33 # 20-15');

INSERT INTO productos VALUES
  (1, 'Café molido 500 g',   32000),
  (2, 'Filtro de tela',       9000),
  (3, 'Café en grano 500 g', 35000),
  (4, 'Molinillo manual',    85000);

INSERT INTO pedidos VALUES
  (1001, '2026-10-05', 1),
  (1002, '2026-10-06', 2),
  (1003, '2026-10-08', 1),
  (1004, '2026-10-09', 3);

INSERT INTO lineas_pedido VALUES
  (1001, 1, 2, 32000),
  (1001, 2, 1,  9000),
  (1002, 1, 1, 32000),
  (1003, 3, 1, 35000),
  (1004, 4, 1, 85000);

La primera línea enciende las claves foráneas, que SQLite trae apagadas en cada conexión [9]; el porqué está en bases de datos relacionales. Con la tienda lo comprobé: sin esa línea, SQLite aceptó un pedido de un cliente que no existe.

La cláusula ON DELETE CASCADE (borrar en cascada) hace que, al borrar un pedido, se borren también sus líneas [9]. Al cliente y a los productos no los toca. Y precio_cobrado no es una copia del precio de lista: si el café sube en diciembre, el pedido de octubre debe seguir diciendo 32.000 pesos.

Cómo reconstruir la tabla original con JOIN

JOIN es la orden de SQL que une las filas de varias tablas por sus claves. Con ella, la tabla de siempre se arma en una vista, que es una consulta guardada con nombre que se usa como si fuera una tabla [10]. El bloque supone que la tabla original sigue en la misma base, para compararlas.

-- La tabla de siempre, armada con un JOIN en vez de copiada
CREATE VIEW vista_pedidos AS
SELECT p.id_pedido AS pedido, pr.nombre AS producto, p.fecha,
       c.nombre AS cliente, c.direccion,
       l.precio_cobrado AS precio, l.cantidad
FROM lineas_pedido AS l
JOIN pedidos   AS p  ON p.id_pedido    = l.id_pedido
JOIN clientes  AS c  ON c.id_cliente   = p.id_cliente
JOIN productos AS pr ON pr.id_producto = l.id_producto;

-- ¿Sobra o falta alguna fila frente a la tabla original?
SELECT COUNT(*) FROM (SELECT * FROM pedidos_planos EXCEPT SELECT * FROM vista_pedidos);
SELECT COUNT(*) FROM (SELECT * FROM vista_pedidos EXCEPT SELECT * FROM pedidos_planos);
-- 0
-- 0

Los dos ceros dicen que no sobra ni falta nada. Es la misma prueba que usó Codd en 1971 para mostrar que su descomposición no perdía información esencial: la tabla original se recuperaba uniendo las nuevas [3]. Quien quiera ver todo junto, como en la hoja de cálculo, consulta la vista. Quien corrige un dato, lo corrige en su tabla.

Prueba final: las tres anomalías desaparecieron

-- 1. Actualización: la dirección se cambia en un solo lugar
UPDATE clientes SET direccion = 'Cra 33 # 48-02' WHERE id_cliente = 1;
SELECT DISTINCT cliente, direccion FROM vista_pedidos WHERE cliente = 'Luz Mery Rueda';
-- Luz Mery Rueda|Cra 33 # 48-02

-- 2. Borrado: cancelar el pedido 1004 borra sus líneas, no a Sofía ni al molinillo
DELETE FROM pedidos WHERE id_pedido = 1004;
SELECT nombre, direccion FROM clientes WHERE id_cliente = 3;
SELECT nombre, precio FROM productos WHERE id_producto = 4;
-- Sofía Gómez|Cl 33 # 20-15
-- Molinillo manual|85000

-- 3. Inserción: un producto nuevo entra sin inventar pedidos
INSERT INTO productos VALUES (5, 'Café descafeinado 250 g', 21000);

-- 4. Y una regla que la tabla plana no tenía: no hay pedidos de clientes que no existen
INSERT INTO pedidos VALUES (1005, '2026-10-10', 99);
-- Falla: FOREIGN KEY constraint failed

La dirección cambió en una sola fila y la vista la muestra en todos los pedidos de Luz Mery. Sofía y el molinillo sobrevivieron a la cancelación, y el descafeinado entró sin pedidos de mentira. De paso apareció una protección que la tabla plana no tenía: el pedido de un cliente que no existe se rechaza.

Reglas de normalización de bases de datos: las seis que se aplican en la práctica

Las reglas o principios de normalización, en su versión práctica, caben en seis frases. Sirven para diseñar una tabla nueva y para revisar una vieja.

  1. Cada tabla trata de un solo tema. Microsoft, en su guía de normalización para Access, su programa de bases de datos de escritorio, pide crear una tabla independiente para cada conjunto de datos relacionados [11].
  2. Cada dato se escribe en un solo lugar. La misma guía pone el ejemplo de la dirección de un cliente: cambiarla es más fácil si solo vive en la tabla de clientes [11].
  3. Cada fila se identifica con una clave primaria [11].
  4. Cada columna que no es clave dice algo de la clave, de toda la clave y de nada más que la clave. Así lo resumió Kent en su guía de 1983 [4].
  5. Los datos parecidos no se reparten en columnas numeradas, como proveedor 1 y proveedor 2: van en otra tabla, una fila por cada uno [11].
  6. Las tablas se conectan con claves foráneas, no copiando datos de una a otra [11].

La cuarta resume las dependencias de este tutorial. Pedir algo de la clave saca la columna que no tiene que ver con la fila. Pedir toda la clave saca la dependencia parcial, como la fecha que solo depende del pedido. Y pedir nada más que la clave saca la transitiva, como la dirección que llega por el cliente.

¿Cuándo no normalizar? La desnormalización y su precio

Desnormalizar es volver a copiar datos a propósito, después de normalizar, para que ciertas consultas sean más rápidas o más simples. Tiene sentido en tablas de informes y de análisis, donde se lee mucho y se escribe poco. El precio es el mismo de la tabla de pedidos: alguien, o algo, tiene que mantener cada copia al día.

Kent lo dejó dicho en su guía. En cuanto al rendimiento, las reglas se inclinan por suponer que todos los campos que no son clave se actualizan con frecuencia [4]. Y tienden a castigar la lectura, porque lo que se podía leer en una fila puede quedar repartido en varias [4]. Por eso escribió: «No hay obligación de normalizar por completo todos los registros cuando se tienen en cuenta los requisitos reales de rendimiento» [4].

Una versión ordenada de esa idea es la vista materializada, que documenta PostgreSQL, otro motor gratuito. Es una copia guardada del resultado de una consulta, que se lee a menudo mucho más rápido que las tablas originales [12]. La documentación de PostgreSQL advierte lo que vale para toda copia: «los datos no siempre están al día; aunque a veces no hacen falta datos actuales» [12]. Su ejemplo propone refrescarla con una tarea programada cada noche [12].

Una copia así se acerca a la redundancia controlada de características de una base de datos. La genera el propio sistema a partir de las tablas originales, no una persona que teclea lo mismo dos veces. Pero solo se pone al día cuando se refresca, y ese refresco hay que programarlo [12].

SituaciónQué se desnormalizaQué se ganaQué cuesta
Tablero de ventas para los vendedoresTotales por vendedor y por día, en una vista materializada [12]Una lectura a menudo mucho más rápida [12]Los datos no siempre están al día; hay que refrescarlos [12]
Modelo de análisis en Power BI, la herramienta de Microsoft para tablerosLas tablas que describen un producto, como categoría y subcategoría, juntas en una sola [13]En general, un modelo más eficiente y más fácil de usar [13]Posiblemente, un modelo más pesado, sobre todo si esas tablas son grandes [13]
Datos que cambian poco, como la ciudad de un clienteSe dejan en la misma tabla; Microsoft sugiere aplicar la regla de sacar los campos que no dependen de la clave solo a lo que cambia con frecuencia [11]Menos tablas pequeñas, que pueden degradar el rendimiento [11]La aplicación debe obligar a revisar los campos relacionados cuando uno cambie [11]

Cómo se ve una base de datos mal normalizada en producción

Una base mal normalizada en producción, es decir, la que ya usa la gente todos los días, no avisa con mensajes de error. El cambio de dirección de Luz Mery funcionó sin una sola queja del motor. Avisa con síntomas: informes que no cuadran entre sí, filas de relleno y correcciones pequeñas que tocan muchas filas. Cada síntoma corresponde a una de las tres anomalías y se puede buscar con una consulta.

SíntomaDe dónde vieneCómo buscarlo
Un cliente o un producto con dos versiones del mismo datoAnomalía de actualizaciónAgrupar por el identificador y contar valores distintos, como en la consulta de las dependencias
Filas de relleno: «SIN CLIENTE», pedido 0, fechas de 1900Anomalía de inserciónBuscar los valores de relleno y contar cuántas filas los usan
Clientes o productos que desaparecen de los informes después de cancelar pedidosAnomalía de borradoComparar la lista de clientes de un mes con la del mes anterior
Columnas numeradas, como teléfono 1, teléfono 2 y teléfono 3Datos parecidos repartidos en columnas: agregar uno más obliga a cambiar la tabla y los programas [11]Revisar la estructura de la tabla y preguntar qué pasa con el cuarto teléfono
Una corrección pequeña que cambia muchas filasAnomalía de actualizaciónMirar cuántas filas reporta el motor después de cada actualización

Ninguno de estos síntomas prueba por sí solo un mal diseño, porque un dato viejo también puede venir de un error al digitar. Pero si la misma consulta encuentra contradicciones mes tras mes, el problema ya no es quien digita. Es la tabla que lo obliga a escribir lo mismo muchas veces.

¿Vale la pena normalizar la base de datos de una tienda pequeña?

Sí, y antes de que crezca. Esto ya es opinión, no regla: normalizar cuesta una tarde y cuatro tablas, y no hacerlo cuesta cada corrección futura. No hace falta comprar nada. El motor de este tutorial es gratuito y el procedimiento cabe en nueve pasos.

Lo que suele faltar es la pregunta de cada columna, y no es una pregunta de ingenieros. La puede responder quien atiende el mostrador, porque es quien sabe si un precio es el de lista o el de la venta. Frente al cliente, la ley ya decidió qué pasa cuando dos precios no cuadran: se paga el más bajo [1]. Dentro de su base de datos, la decisión es suya, y se toma una sola vez, al diseñar.

Preguntas frecuentes

¿Normalizar una base de datos la hace más lenta? Puede hacer más lentas algunas lecturas, porque unir cuatro tablas cuesta más trabajo que leer una [4]. A cambio, cada corrección toca una sola fila y las claves protegen lo que se escribe. Si una consulta concreta se vuelve lenta, se puede guardar solo su resultado en una vista materializada, sin desarmar el resto del diseño [12].

¿En PostgreSQL también hay que activar las claves foráneas? No. En PostgreSQL, una vez declarada la clave foránea, el motor impide guardar filas que apunten a algo inexistente. Su documentación lo muestra con pedidos de productos que no están en la tabla de productos [14]. La protección no cubre las celdas vacías, así que la columna que apunta a otra tabla conviene declararla obligatoria, con NOT NULL [14].

¿Se puede normalizar una tabla de Excel? Sí, con el mismo procedimiento: una hoja por tema y una columna de código para unirlas. La guía de Microsoft para Power BI, su herramienta de análisis y tableros, advierte que un archivo exportado probablemente trae los datos desnormalizados. Recomienda repartirlos en varias tablas con Power Query, su herramienta para transformar datos [13]. Cuándo conviene dejar Excel por una base de datos es tema de base de datos en Excel.

¿Cómo normalizar una base de datos que ya está en uso? Primero busque contradicciones con una consulta como la de las dependencias: si una clienta tiene dos direcciones, alguien debe decidir cuál vale antes de mover nada. Después cree las tablas nuevas y llénelas desde la vieja copiando los valores distintos, con SELECT DISTINCT. Si se salta el primer paso, esa copia deja a la misma clienta dos veces, una por cada dirección. Por último, cree una vista que entregue las columnas de la tabla antigua, para que los informes que la leían sigan funcionando [10].

¿Dónde practicar ejercicios de normalización? Empiece con la tabla de esta página: agréguele una columna de categoría, con valores como café y accesorios, y pregúntese de qué depende. Depende del producto, así que se muda a la tabla de productos. Para practicar con datos más grandes, abiertos y con licencia clara, hay una selección en bases de datos para practicar.

Referencias

  1. Congreso de la República de Colombia. Ley 1480 de 2011, por medio de la cual se expide el Estatuto del Consumidor y se dictan otras disposiciones (12 de octubre de 2011; Diario Oficial 48.220; rige desde el 12 de abril de 2012) — art. 26, información pública de precios: el precio debe informarse visualmente y «el consumidor solo estará obligado a pagar el precio anunciado»; «En el evento de que aparezcan dos (2) o más precios, que existan tachaduras o enmendaduras, el consumidor sólo estará obligado al pago del precio más bajo de los que aparezcan indicados, sin perjuicio de las sanciones a que hubiere lugar». Secretaría del Senado: Ley 1480 de 2011 — Consultada: 2026-10-24.
  2. Universidad de Granada. Normalización, apuntes de la asignatura Programación de Bases de Datos (Ingeniería Técnica de Gestión), curso 2007-2008 — el diseño lógico puede tener defectos conceptuales y deficiencias que provoquen redundancia de datos y falta de restricciones semánticas; la normalización refina el diseño y da como resultado «un nuevo conjunto de relaciones ampliado respecto al original». Universidad de Granada: apuntes de normalización (PDF) — Consultada: 2026-10-24.
  3. Codd, Edgar F. Further Normalization of the Data Base Relational Model, informe de investigación RJ909, IBM Research Laboratory, San José (California), 31 de agosto de 1971 — objetivos de la normalización, el primero «To free the collection of relations from undesirable insertion, update and deletion dependencies», y entre los otros reducir la reestructuración al llegar tipos de datos nuevos (alargando la vida de los programas) y hacer el modelo más informativo para los usuarios; definición de dependencia funcional («at every instant of time, each value in A has no more than one value in B associated with it»); ejemplo del proveedor que se muda de Poole a Tolpuddle, con «the number of tuples to be updated can, and usually will, change with time»; ejemplo de dependencia de borrado y «It is left to the reader to illustrate a corresponding insertion dependency»; tras la descomposición no se pierde información esencial porque la relación original se recupera con la unión natural. Texto leído en la copia escaneada del informe que aloja el foro del proyecto The Third Manifesto. Copia del informe RJ909 de Codd (PDF) — Consultada: 2026-10-24.
  4. Kent, William. A Simple Guide to Five Normal Forms in Relational Database Theory, Communications of the ACM, vol. 26, n.º 2, febrero de 1983, pp. 120-125 (DOI 10.1145/358024.358054) — «The normalization rules are designed to prevent update anomalies and data inconsistencies»; las pautas suponen que todos los campos que no son clave se actualizan con frecuencia y tienden a penalizar la recuperación, porque lo que estaba en un registro pasa a varios; «There is no obligation to fully normalize all records when actual performance requirements are taken into account»; un campo que no es clave debe dar un hecho sobre la clave, toda la clave y nada más que la clave. Texto leído en la copia del sitio del autor, con el aviso de derechos de la ACM. William Kent: A Simple Guide to Five Normal Forms — Consultada: 2026-10-24.
  5. scikit-learn developers. Preprocessing data, documentación de scikit-learn 1.9.1, sección 8.3 — «Normalization is the process of scaling individual samples to have unit norm»; como alternativa, escalar las variables para que queden entre un mínimo y un máximo, «often between zero and one». scikit-learn: Preprocessing data — Consultada: 2026-10-24.
  6. SQLite. Release History Of SQLite — la versión 3.53.1 se publicó el 5 de mayo de 2026. SQLite: historial de versiones — Consultada: 2026-10-24.
  7. SQLite. CREATE TABLE (página actualizada el 30 de abril de 2025) — «According to the SQL standard, PRIMARY KEY should always imply NOT NULL»; por un error de algunas versiones tempranas no es así en SQLite, que admite valores NULL en la mayoría de las columnas de clave primaria salvo que se declaren NOT NULL (o sean INTEGER PRIMARY KEY, o la tabla sea WITHOUT ROWID o STRICT); corregirlo podría romper aplicaciones antiguas. SQLite: CREATE TABLE — Consultada: 2026-10-24.
  8. Codd, Edgar F. A Relational Model of Data for Large Shared Data Banks, Communications of the ACM, vol. 13, n.º 6, junio de 1970, pp. 377-387 (DOI 10.1145/362384.362685) — sección 2.3, consistencia: si el sistema de información carece de información semántica detallada sobre cada relación, «and it most probably will», no puede deducir las redundancias aplicables al conjunto de relaciones. Texto leído en la copia que aloja la Universidad de Pensilvania. Universidad de Pensilvania: copia del artículo de Codd (1970) — Consultada: 2026-10-24.
  9. SQLite. SQLite Foreign Key Support (página actualizada el 20 de marzo de 2026) — claves foráneas desde la versión 3.6.19 (2009); «Foreign key constraints are disabled by default (for backwards compatibility), so must be enabled separately for each database connection», con PRAGMA foreign_keys = ON; la acción ON DELETE CASCADE borra en la tabla hija las filas asociadas a la fila borrada de la tabla padre. SQLite: Foreign Key Support — Consultada: 2026-10-24.
  10. SQLite. CREATE VIEW — la orden asigna un nombre a una consulta SELECT ya armada; una vez creada, la vista se puede usar en la cláusula FROM de otra consulta en lugar del nombre de una tabla; en SQLite las vistas son de solo lectura. SQLite: CREATE VIEW — Consultada: 2026-10-24.
  11. Microsoft. Descripción de los conceptos básicos de normalización de la base de datos (se aplica a Access 2021, 2019, 2016 y 2013; artículo fechado el 26 de mayo de 2025) — normalizar incluye crear tablas y relaciones según reglas que protegen los datos y eliminan la redundancia y la dependencia incoherente; un cambio de dirección de cliente es más fácil si ese dato solo está en la tabla de clientes; crear una tabla independiente para cada conjunto de datos relacionados e identificarlo con una clave principal; no usar varios campos para datos similares (código de proveedor 1 y 2), porque un tercero exige modificar programas y tablas; relacionar tablas con una clave externa; muchas tablas pequeñas pueden degradar el rendimiento, puede ser más factible aplicar la tercera regla solo a los datos que cambian con frecuencia, y si quedan campos dependientes la aplicación debe obligar a verificar todos los campos relacionados cuando cambie uno. Microsoft Learn: conceptos básicos de normalización — Consultada: 2026-10-24.
  12. The PostgreSQL Global Development Group. PostgreSQL 18 Documentation, sección 39.3 «Materialized Views» — la vista materializada guarda los resultados en forma de tabla y se pone al día con REFRESH MATERIALIZED VIEW; el acceso a sus datos «is often much faster» que a las tablas de origen, pero «the data is not always current; yet sometimes current data is not needed»; ejemplo de un resumen de ventas por vendedor y fecha que una tarea programada refresca cada noche. PostgreSQL: Documentation 18, Materialized Views — Consultada: 2026-10-24.
  13. Microsoft. Understand star schema and the importance for Power BI, documentación de Power BI (artículo fechado el 30 de diciembre de 2024) — un archivo exportado o extracto de datos probablemente representa un conjunto desnormalizado, que se recomienda transformar con Power Query en varias tablas normalizadas; en una dimensión copo de nieve, «Generally, the benefits of a single model table outweigh the benefits of multiple model tables», y guardar datos redundantes desnormalizados puede aumentar el tamaño del modelo, sobre todo en tablas de dimensión grandes. Microsoft Learn: star schema en Power BI — Consultada: 2026-10-24.
  14. The PostgreSQL Global Development Group. PostgreSQL 18 Documentation, sección 5.5.5 «Foreign Keys» — una clave foránea exige que los valores de una columna coincidan con los de alguna fila de otra tabla; con ella «it is impossible to create orders with non-NULL product_no entries that do not appear in the products table». PostgreSQL: Documentation 18, Constraints — Consultada: 2026-10-24.
Cómo investigamos esta página
  • Pregunta que responde: Por qué se normaliza una base de datos y cómo se hace paso a paso, sobre una tabla de pedidos que falla de verdad.
  • Fuentes: identificadas 28 → incluidas 14
  • Criterios de inclusión: textos originales de Codd y Kent, documentación oficial de SQLite, PostgreSQL y Microsoft, apuntes universitarios y la ley colombiana; nada de blogs ni enciclopedias.
  • Fecha de corte de los datos: 2026-10-24
Scroll to Top