Estructura tus esquemas de 1NF a 3NF para eliminar anomalías, y aprende cuándo desnormalizar con conocimiento de causa.
Abrir esta lección en KodokonUn mal esquema te cuesta años. La normalización persigue una sola cosa: que cada dato se almacene una única vez, para eliminar tres anomalías: de actualización (cambiar un correo en diez sitios), de inserción (no poder añadir un producto sin un pedido) y de eliminación (borrar el último pedido borra al cliente). Observa este esquema deliberadamente chapucero.
CREATE TABLE bad_orders (
id INTEGER PRIMARY KEY,
customer_name TEXT,
customer_email TEXT,
product_names TEXT,
total REAL
);
INSERT INTO bad_orders
(customer_name, customer_email,
product_names, total)
VALUES
('Alice', 'alice@mail.com',
'keyboard,mouse', 74.0),
('Alice', 'alice@mail.co',
'monitor', 199.0);Las tres primeras formas normales, en la práctica. 1NF: valores atómicos - product_names = 'keyboard,mouse' la viola, lo que vuelve impracticable cualquier WHERE o JOIN sobre los productos. 2NF (clave compuesta): cada columna depende de la clave completa - almacenar el precio de catálogo de un producto en una línea de pedido identificada por (pedido, producto) la viola, porque el precio depende solo del producto. 3NF: ninguna columna que no sea clave depende de otra columna que no sea clave - customer_email, que depende de customer_name, la viola; de ahí el duplicado alice@mail.co. Aquí está el esquema corregido.
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL NOT NULL
);
CREATE TABLE purchases (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL
REFERENCES customers(id)
);
CREATE TABLE purchase_items (
purchase_id INTEGER NOT NULL
REFERENCES purchases(id),
product_id INTEGER NOT NULL
REFERENCES products(id),
quantity INTEGER NOT NULL,
PRIMARY KEY (purchase_id, product_id)
);PRAGMA foreign_keys = ON;
INSERT INTO customers (name, email)
VALUES ('Alice', 'alice@mail.com');
INSERT INTO products (name, price)
VALUES ('keyboard', 49.0), ('mouse', 25.0);
INSERT INTO purchases (customer_id) VALUES (1);
INSERT INTO purchase_items
(purchase_id, product_id, quantity)
VALUES (1, 1, 1), (1, 2, 1);
SELECT c.name, p.name AS product,
i.quantity, p.price
FROM purchase_items AS i
JOIN purchases AS pu ON pu.id = i.purchase_id
JOIN customers AS c ON c.id = pu.customer_id
JOIN products AS p ON p.id = i.product_id;Entonces, ¿cuándo deberías desnormalizar? Cuando las lecturas se convierten en el cuello de botella medido: uniones de cinco tablas en una pantalla crítica, una agregación recalculada en cada renderizado. Los patrones honestos: una columna de caché (purchases.total congelada en el momento del pedido - y con razón: el precio pagado es un dato histórico, distinto del precio de catálogo), un contador mantenido por un trigger, o una tabla de reportes reconstruida por lotes. El precio a pagar: cada copia puede desviarse, y ahora es tu código, no el motor, el que garantiza la consistencia.