Structurez vos schémas de la 1NF à la 3NF pour éliminer les anomalies, et sachez quand dénormaliser en connaissance de cause.
Ouvrir cette leçon dans KodokonUn mauvais schéma se paie pendant des années. La normalisation vise une chose : que chaque fait ne soit stocké qu'une seule fois, pour éliminer trois anomalies : de mise à jour (modifier un e-mail à dix endroits), d'insertion (impossible d'ajouter un produit sans commande) et de suppression (supprimer la dernière commande efface le client). Observez ce schéma volontairement raté.
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);Les trois premières formes normales, en pratique. 1NF : valeurs atomiques - product_names = 'keyboard,mouse' la viole, rendant tout WHERE ou JOIN sur les produits impraticable. 2NF (clé composée) : chaque colonne dépend de toute la clé - stocker le prix catalogue du produit dans une ligne de commande identifiée par (commande, produit) la viole, car le prix ne dépend que du produit. 3NF : aucune colonne non clé ne dépend d'une autre colonne non clé - customer_email qui dépend de customer_name la viole ; d'où le doublon alice@mail.co. Voici le schéma corrigé.
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;Alors, quand dénormaliser ? Quand la lecture devient le goulot mesuré : jointures à cinq tables sur un écran critique, agrégat recalculé à chaque affichage. Les patterns honnêtes : une colonne de cache (purchases.total figé à la commande - d'ailleurs légitime : le prix payé est un fait historique, distinct du prix catalogue), un compteur maintenu par trigger, ou une table de reporting reconstruite par batch. Le prix à payer : chaque copie peut diverger, et c'est votre code qui devient garant de la cohérence, plus le moteur.