Léa est en 2A, puis en 1A
La normalisation est la méthode qui range chaque information une seule fois dans une base de données, pour éviter les répétitions et les contradictions.
Elle évite par exemple qu'une élève soit en classe 2A dans une ligne et en 1A dans une autre. Aujourd'hui, tu provoques ce défaut exprès, puis tu le répares.
À la fin de cette leçon
Remarque
Je peux enlever les données répétées d'une table en la découpant en tables reliées.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Redondance | la même information rangée à plusieurs endroits |
| Anomalie de mise à jour | une information changée à un endroit et oubliée ailleurs |
| Normalisation | découper les tables pour ranger chaque fait une seule fois |
| Première forme normale | une seule valeur dans chaque case |
| Deuxième forme normale | chaque colonne dépend de toute la clé |
| Troisième forme normale | chaque colonne dépend de la clé et de rien d'autre |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une information répétée finit par se contredire
Le foyer des élèves note chaque commande dans un seul grand tableau. À chaque ligne, il recopie la classe du client et le prix du produit.
Quand une élève change de classe, il faut corriger toutes ses lignes. Si on en oublie une, la base se contredit : c'est une anomalie de mise à jour.
Avant de commencer
Remarque
Appuie sur Start the lab dans la barre en haut et attends que la machine soit prête. Les deux Runs suivants servent à te connecter. Le deuxième crée un alias : un nom court, sql, pour une longue commande.
Accepter le certificat du serveur
printf '[client]\nssl-verify-server-cert=0\n' > ~/.my.cnfAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Un nom court pour la boutique
alias sql='mariadb -h db -u student -pstudent shop -t -e'Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Créer ta base du foyer
sql "CREATE DATABASE IF NOT EXISTS student_foyer"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Un nom court pour elle
alias foyer='mariadb -h db -u student -pstudent student_foyer -t -e'Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Créer le grand tableau et le remplir
foyer "DROP TABLE IF EXISTS commandes_plat; CREATE TABLE commandes_plat (id INT AUTO_INCREMENT PRIMARY KEY, client VARCHAR(30) NOT NULL, classe VARCHAR(10) NOT NULL, produit VARCHAR(30) NOT NULL, prix DECIMAL(5,2) NOT NULL, quantite INT NOT NULL); INSERT INTO commandes_plat (client, classe, produit, prix, quantite) VALUES ('Lea', '2A', 'Jus', 1.50, 2), ('Lea', '2A', 'Sandwich', 3.20, 1), ('Hugo', '1B', 'Jus', 1.50, 1), ('Hugo', '1B', 'Cookie', 1.20, 3), ('Nina', '2A', 'Sandwich', 3.20, 2), ('Nina', '2A', 'Jus', 1.50, 1)"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Voir ce qui se répète
foyer "SELECT client, classe, produit, prix FROM commandes_plat ORDER BY id"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Changer Léa de classe sur une seule ligne
foyer "UPDATE commandes_plat SET classe = '1A' WHERE id = 1"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
La base se contredit
foyer "SELECT DISTINCT client, classe FROM commandes_plat ORDER BY client, classe"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Trois questions guident le découpage
On découpe une table en posant trois questions, dans l'ordre. Ce sont les trois premières formes normales.
Première : y a-t-il une seule valeur dans chaque case ? Deuxième : chaque colonne dépend-elle de toute la clé ? Troisième : chaque colonne dépend-elle de la clé et de rien d'autre ?
Exemple : une règle, un défaut, un remède
| Forme | Défaut | Remède |
|---|---|---|
| Première | Une case contient une liste : « Jus, Cookie » | Une ligne par produit |
| Deuxième | La clé est (commande, produit) et le prix ne dépend que du produit | Le prix va dans une table produits |
| Troisième | La classe dépend du client, pas de la commande | La classe va dans une table clients |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
On découpe en trois tables reliées
Le grand tableau devient trois tables. clients garde la classe de chaque client une seule fois. produits garde le prix de chaque produit une seule fois.
commandes ne garde que deux clés étrangères et la quantité. Un JOIN recolle le tout quand on en a besoin.
Créer les trois tables
foyer "DROP TABLE IF EXISTS commandes, clients, produits; CREATE TABLE clients (id INT AUTO_INCREMENT PRIMARY KEY, nom VARCHAR(30) NOT NULL, classe VARCHAR(10) NOT NULL); CREATE TABLE produits (id INT AUTO_INCREMENT PRIMARY KEY, nom VARCHAR(30) NOT NULL, prix DECIMAL(5,2) NOT NULL); CREATE TABLE commandes (id INT AUTO_INCREMENT PRIMARY KEY, client_id INT NOT NULL, produit_id INT NOT NULL, quantite INT NOT NULL, FOREIGN KEY (client_id) REFERENCES clients(id), FOREIGN KEY (produit_id) REFERENCES produits(id))"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Ranger chaque fait une seule fois
foyer "INSERT INTO clients (nom, classe) VALUES ('Lea', '2A'), ('Hugo', '1B'), ('Nina', '2A'); INSERT INTO produits (nom, prix) VALUES ('Jus', 1.50), ('Sandwich', 3.20), ('Cookie', 1.20); INSERT INTO commandes (client_id, produit_id, quantite) VALUES (1, 1, 2), (1, 2, 1), (2, 1, 1), (2, 3, 3), (3, 2, 2), (3, 1, 1)"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Recoller le grand tableau avec JOIN
foyer "SELECT c.nom, c.classe, p.nom AS produit, p.prix, o.quantite FROM commandes o JOIN clients c ON c.id = o.client_id JOIN produits p ON p.id = o.produit_id ORDER BY o.id"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Changer Léa de classe, une seule fois
foyer "UPDATE clients SET classe = '1A' WHERE nom = 'Lea'; SELECT c.nom, c.classe, p.nom AS produit FROM commandes o JOIN clients c ON c.id = o.client_id JOIN produits p ON p.id = o.produit_id WHERE c.nom = 'Lea' ORDER BY o.id"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une commande pour un client qui n'existe pas
foyer "INSERT INTO commandes (client_id, produit_id, quantite) VALUES (9, 1, 1)"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une erreur fréquente
Attention
Beaucoup pensent que découper une table fait perdre le lien entre les informations. Le lien reste : la clé étrangère garde le numéro, et un JOIN recolle les morceaux. On découpe pour ranger chaque fait une fois, pas pour séparer ce qui va ensemble.
À toi : extraire la table des clients
-- La liste des clients telle qu'elle serait dans une table clients.
SELECT DISTINCT client, classe
FROM commandes_plat
ORDER BY client;
-- Fais la même liste pour les produits et leur prix.