Bases de données & SQL · L2 · Section 6/11
Normalisation
Progression
#Normalisation : éliminer la redondance
Objectif : réduire les redondances et les anomalies d'insertion, de mise à jour et de suppression grâce aux formes normales (1NF, 2NF, 3NF, BCNF) et à la décomposition sans perte. La normalisation rend les données cohérentes et plus faciles à maintenir ; on ne dénormalise ensuite, de façon mesurée, que si des lectures spécifiques l'exigent.
#Prérequis et objectifs
- Prérequis : Modélisation (clés primaires, étrangères, associations N-N).
- Définir et repérer une dépendance fonctionnelle.
- Diagnostiquer la forme normale d'une table et les anomalies qu'elle provoque.
- Décomposer une table en 3NF/BCNF sans perte d'information et en préservant les dépendances.
#Vocabulaire : deux définitions suffisent
- Dépendance fonctionnelle (DF) :
X → Ysignifie que la valeur de X détermine celle de Y. Deux lignes partageant X partagent nécessairement Y. Exemple :isbn → titre. - Clé candidate : ensemble minimal d'attributs qui détermine tous les autres. Une super-clé est un ensemble déterminant tous les autres, pas forcément minimal.
Les définitions formelles du cours, à connaître mot pour mot :
Soient A et B des attributs d'une relation. B est fonctionnellement dépendant de A (ce qui s'écrit ) si chaque valeur de A est associée à exactement une valeur de B.
Soient A et B des attributs d'une relation. B est fonctionnellement complètement dépendant de A si B dépend fonctionnellement de A mais ne dépend d'aucun sous-ensemble propre de A.
La nuance « aucun sous-ensemble propre » est le cœur de la 2NF. Si A est un ensemble de trois attributs {A1, A2, A3} et que A1 → B, alors B dépend de A mais aussi d'une partie de A : la dépendance est partielle, pas complète.
#Comment on identifie une DF sur un échantillon
Un énoncé donne souvent un tableau de valeurs et demande d'en déduire les DF. La règle est une lecture en colonnes : si pour deux lignes quelconques, des valeurs égales dans la colonne X impliquent des valeurs égales dans la colonne Y, alors X → Y. Dit autrement, X admet une injection sur Y.
Sur un échantillon, l'absence de contre-exemple est une présomption, pas une preuve — c'est la limite que tout énoncé de ce type comporte. On ne peut que proposer les DF compatibles avec les données, en supposant l'échantillon représentatif de toutes les valeurs possibles.
Exemple type, sur une table Echantillon(A, B, C, D, E) :
| A | B | C | D | E |
|---|---|---|---|---|
| a | b | z | w | q |
| e | b | r | w | p |
| a | d | z | w | t |
| e | d | r | w | q |
| a | f | z | s | t |
| e | f | r | s | t |
Lecture : A prend deux valeurs (a, e) ; à a correspondent toujours C = z et D = w, à e correspondent toujours C = r et D = w. Donc et . En revanche a est associé à b puis à d puis à f : . Symétriquement, et .
La clé primaire est le plus petit ensemble d'attributs qui détermine fonctionnellement tous les autres. Ici, ni A ni C ni D ne déterminent B ou E ; la clé est donc composite, et c'est le plus petit ensemble qui fonctionne.
#Les formes normales, une par une
- 1NF : valeurs atomiques. Pas de liste dans une colonne, pas de groupe répétitif (
tel1,tel2,tel3). - 2NF : 1NF, et aucun attribut non-clé ne dépend d'une partie de la clé composite. Ne peut être violée qu'avec une clé composite.
- 3NF : 2NF, et aucune dépendance transitive : un attribut non-clé ne dépend pas d'un autre attribut non-clé.
- BCNF : pour toute DF non triviale
X → Y, X est une super-clé. Strictement plus exigeante que 3NF.
Moyen mnémotechnique classique : « la clé, toute la clé, rien que la clé » ; 2NF porte sur « toute la clé », 3NF sur « rien que la clé ».
#Les définitions officielles
Première forme normale (1NF) : une relation est en 1NF si elle ne contient pas d'attributs à valeurs multiples ou composites, c'est-à-dire si l'intersection de toute ligne et de toute colonne ne comporte qu'une et une seule valeur.
Deuxième forme normale (2NF) : une relation est en 2NF si elle est en 1NF et si tout attribut de clé non primaire est fonctionnellement complètement dépendant de la clé primaire.
Troisième forme normale (3NF) : une relation est en 3NF si elle est en 2NF et si aucun attribut ne faisant pas partie de la clé primaire ne dépend transitivement de la clé primaire.
Deux précisions que la définition courte de la 1NF laisse échapper, et qui font la différence en TD :
- La 1NF concerne les lignes autant que les attributs. Un attribut viole la 1NF s'il porte plusieurs valeurs pour une ligne (attribut multivalué : une colonne
téléphonesavec « 06…, 07… ») et s'il porte plusieurs informations distinctes dans une seule valeur (attribut composite : une colonneBatEtEsccontenant « A,1 » pour le bâtiment A et l'escalier 1). Le remède est le même dans les deux cas : découper en colonnes atomiques, ou créer une table séparée si le nombre de valeurs varie. - Une relation en 2NF peut violer la 3NF. Les deux formes sont indépendantes dans ce sens : la 2NF traite la dépendance à une partie de la clé, la 3NF traite la dépendance à un autre attribut non-clé. Une relation peut donc être en 2NF et pas en 3NF — c'est même le cas le plus fréquent en pratique.
#Partielle ou transitive : le diagnostic
Sur une relation de clé primaire (A, B, C), trois DF possibles se lisent très différemment :
1A, B, C -> D D dépend complètement de la clé → conforme 2NF2A, B -> E E dépend partiellement de la clé → viole 2NF3A, F -> G G dépend d'un attribut non-clé (F) → viole 3NFLe troisième cas mérite attention : si F n'appartient pas à la clé, alors G dépend transitivement de la clé via F. Corriger la 2NF ne le corrige pas — il faut traiter la 3NF séparément.
Transformer une relation en 2NF signifie créer autant de tables supplémentaires qu'il y a de dépendances partielles distinctes, chacune contenant la partie de clé concernée et les attributs qu'elle détermine. Transformer une relation en 3NF signifie soit ajouter l'attribut concerné à la table appropriée, soit créer une nouvelle table contenant cet attribut et l'attribut non-clé qui le détermine — lequel devient clé étrangère de la table d'origine.
#Animation : décomposition sans perte
#Étude de cas : la table factures
Table initiale : factures(num, client, ville, ville_cp, total), avec la DF ville → ville_cp (chaque ville a un code postal unique).
Anomalies concrètes :
- Insertion : impossible d'enregistrer le code postal d'une ville sans client, puisqu'il faut un numéro de facture (clé).
- Mise à jour : le code postal de Nice figure sur mille factures ; le changer demande mille mises à jour, et une seule omission crée une incohérence.
- Suppression : supprimer la dernière facture d'une ville efface aussi la connaissance de son code postal.
Décomposition :
1create table villes(2 nom text primary key,3 code_postal text not null4);5 6create table factures(7 num integer primary key,8 client text not null,9 ville text not null references villes(nom),10 total real not null11);#Pas-à-pas : vérifier la décomposition
La DF ville → ville_cp transporte une connaissance (géographie) qui ne dépend ni du numéro de facture ni du client : elle viole 3NF dans factures.
#Cas 2NF : la clé composite
Table ligne_commande(commande_id, produit, libelle_produit, quantite), clé (commande_id, produit). La DF produit → libelle_produit ne porte que sur une partie de la clé : violation 2NF. Le libellé d'un produit se répète sur toutes ses lignes de commande ; le corriger demande autant de mises à jour que d'occurrences.
Décomposition : produits(produit, libelle) et ligne_commande(commande_id, produit, quantite). La jointure sur produit reconstitue tout, sans perte.
#Cas BCNF : quand 3NF ne suffit plus
Table inscriptions(etudiant, cours, enseignant), avec les règles : un étudiant s'inscrit à des cours, chacun auprès d'un enseignant ; chaque enseignant enseigne un seul cours. DF : etudiant, cours → enseignant et enseignant → cours. Clés candidates : (etudiant, cours) et (etudiant, enseignant). Tous les attributs appartiennent à une clé candidate : la table est 3NF. Mais le déterminant enseignant seul n'est pas une super-clé : BCNF est violée, et si un enseignant change de cours, il faut corriger toutes ses inscriptions.
Décomposition BCNF : enseignements(enseignant primary key, cours) et inscriptions(etudiant, enseignant). La DF etudiant, cours → enseignant n'est plus contrôlable dans une seule table : c'est le compromis à connaître, BCNF n'offre pas toujours la préservation des DF alors que 3NF la garantit.
#Propriétés souhaitables d'une décomposition
- Sans perte : la jointure naturelle des tables résultantes reconstitue exactement la table d'origine. Non négociable.
#Exercice : normaliser une table de réservations
Table : reservations(id, nom_client, email_client, hotel, ville_hotel, etoiles_hotel, date_arrivee, nb_nuits).
DF identifiées : id → tous les attributs ; hotel → ville_hotel, etoiles_hotel ; email_client → nom_client.
- Listez les anomalies d'insertion, de mise à jour et de suppression.
- Donnez la décomposition en 3NF, sans perte.
- Vérifiez que chaque DF est préservée.
#Exercice type examen 1 : les anomalies de PersonnelFiliale
Table : PersonnelFiliale(numPersonnel, nomP, fonction, salaire, numFiliale, adresseF).
| numPersonnel | nomP | fonction | salaire | numFiliale | adresseF |
|---|---|---|---|---|---|
| 12 | Blanc | gérant | 30000 | 5 | 67 Champs-Élysée, Paris |
| 37 | Bertrand | assistant | 12000 | 3 | 243 rue des Roses, Montréal |
| 14 | Renault | superviseur | 18000 | 3 | 243 rue des Roses, Montréal |
| 9 | Thale | assistant | 9000 | 7 | 3 rue de la Buffa, Nice |
| 45 | Brière | gérant | 24000 | 3 | 243 rue des Roses, Montréal |
| 41 | Hette | assistant | 9000 | 5 | 67 Champs-Élysée, Paris |
Décrivez les anomalies d'insertion, de suppression et de modification.
Correction détaillée
Le diagnostic commence par une observation structurelle : la définition de la filiale se trouve dans la même table que le personnel. La DF fautive est , et numFiliale n'est pas la clé primaire de la table.
- Insertion : ajouter un membre du personnel exige de saisir une adresse de filiale. On ne peut pas enregistrer une filiale sans y affecter quelqu'un — une filiale nouvellement créée et encore vide est impossible à représenter. Pire, rien n'empêche de saisir une adresse différente de celle enregistrée pour la même filiale dans une autre ligne, ce qui casse la cohérence avec
numFiliale. - Modification : changer l'adresse d'une filiale oblige à la corriger en cascade sur toutes les lignes du personnel qui y sont rattachées. Une seule omission suffit à créer deux adresses contradictoires pour la filiale 3.
- Suppression : supprimer le dernier employé d'une filiale efface du même coup la connaissance de l'adresse de cette filiale.
Le problème général : la filiale n'a pas de table séparée et se trouve contrainte de dépendre des employés.
Solution : créer une table Filiale(numFiliale, adresseF) et transformer numFiliale de PersonnelFiliale en clé étrangère. Selon la sémantique retenue, on conserve la table d'association PersonnelFiliale(numPersonnel#, numFiliale#) si un employé peut changer de filiale au cours du temps, ou l'on met directement la clé étrangère dans Personnel(numPersonnel, nomP, fonction, salaire, numFiliale#) si l'affectation est définitive. La deuxième forme suppose la cardinalité « chaque personnel travaille dans une seule filiale » ; la première est la plus générale.
#Exercice type examen 2 : la table des rendez-vous dentaires
C'est l'exercice de normalisation le plus complet du cours : il traverse les trois formes normales sur un cas réaliste. Table initiale (une ligne par rendez-vous) :
| numPraticien | nomDentiste | numPatient | nomPatient | rendezVous | numCabinet |
|---|---|---|---|---|---|
| S1011 | Fastrez | P100 | Blanc | 9/12/2012 10h00 | C15 |
| S1011 | Fastrez | P105 | Denaux | 9/12/2012 12h00 | C15 |
| S1024 | Maréchal | P108 | Piette | 9/12/2012 10h00 | C10 |
| S1024 | Maréchal | P108 | Piette | 9/14/2012 14h00 | C10 |
| S1032 | Meier | P105 | Denaux | 9/14/2012 16h30 | C15 |
| S1032 | Meier | P110 | Marchand | 9/15/2012 18h00 | C13 |
Règles métier : un patient reçoit un rendez-vous à une heure et une date précises chez le dentiste qui opère dans un cabinet précis. Chaque jour de consultation, un dentiste est affecté à un cabinet dentaire précis. Un patient ne peut pas être examiné par un même dentiste plus d'une fois par jour.
- Identifiez la clé primaire.
- Identifiez les dépendances fonctionnelles.
- Normalisez en 3NF.
- Ajoutez l'attribut
prestationCabinetet re-normalisez.
Correction détaillée
1. Clé primaire. Un patient ne peut pas voir un même dentiste deux fois le même jour : le triplet (numPraticien, numPatient, date) identifie donc un rendez-vous de façon unique. Attention à l'heure : deux rendez-vous du même jour pour le même patient chez le même dentiste sont interdits, donc l'heure n'appartient pas à la clé. La clé primaire est (numPraticien, numPatient, date).
2. Dépendances fonctionnelles. L'énoncé fournit l'information en clair, il ne reste qu'à la traduire :
1numPraticien, numPatient, date -> heure, numCabinet (et chacun pris séparément aussi)2numPraticien -> nomDentiste3numPatient -> nomPatient4numPraticien, date -> numCabinetLa lecture décisive : tous les attributs ne dépendent pas de la clé primaire. nomDentiste ne dépend que de numPraticien, nomPatient ne dépend que de numPatient, et numCabinet ne dépend que de (numPraticien, date). Ce sont des dépendances partielles : la clé est composite et plusieurs attributs ne dépendent que d'une partie de cette clé.
3. Normalisation en 3NF.
1NF. La table n'est pas en 1NF : l'attribut rendezVous est composite, il contient à la fois une date et une heure. On le scinde en deux attributs, date et heure. Une fois scindé, chaque cellule ne porte plus qu'une valeur atomique et la table est en 1NF — mais elle reste redondante : « Fastrez » est répété, « Maréchal » aussi, et l'adresse de chaque patient se répète à chaque rendez-vous.
2NF. On identifie tous les attributs qui dépendent partiellement et uniquement d'une partie de la clé : numPraticien → nomDentiste, numPatient → nomPatient, (numPraticien, date) → numCabinet. Ces trois dépendances partielles imposent la création de tables séparées, reliées par des clés étrangères.
1PraticienPatient(numPraticien#, numPatient#, date, heure) -- clé (numPraticien, numPatient, date)2Dentiste(numPraticien, nomDentiste)3Patient(numPatient, nomPatient)4Cabinet(numPraticien#, date, numCabinet)Chaque attribut admet désormais une dépendance fonctionnelle complète avec la clé primaire de sa table : la 2NF est atteinte.
3NF. La table est déjà en 3NF : tous les attributs sont en 2NF et aucun n'a de dépendance transitive avec la clé primaire. Aucune nouvelle décomposition n'est nécessaire — savoir s'arrêter fait partie de la réponse.
4. Ajout de prestationCabinet. Si la prestation dépend du cabinet et non du rendez-vous, elle appartient à la table Cabinet : Cabinet(numPraticien#, date, numCabinet, prestationCabinet). C'est la réponse attendue : il suffit d'ajouter l'attribut dans la table appropriée, sans créer de nouvelle table.
Le cas symétrique mérite d'être noté : si prestationCabinet dépendait du numéro de cabinet seul (et non du couple praticien-date), alors numCabinet → prestationCabinet serait une dépendance transitive et il faudrait créer une table Cabinet(numCabinet, prestationCabinet) séparée. La question de l'attribut détermine donc la structure — c'est exactement ce qu'un énoncé d'examen veut vous faire dire.
#Exercice type examen 3 : la médiathèque
Table initiale. Dans l'énoncé d'origine, numUtilis et nomUtilis sont fusionnés sur deux lignes : chaque utilisateur a emprunté deux DVD, et les deux premières colonnes ne sont écrites qu'une fois. C'est précisément cette fusion qui matérialise l'attribut multivalué.
| numUtilis | nomUtilis | codeDvd | titreDvd | genreDvd | dateEmprunt | dateRetour | numRealis | nomRealis |
|---|---|---|---|---|---|---|---|---|
| 48562 | Richard | 185634 | La Chèvre | comique | 01/12/11 | 1/21/2011 | 14564 | Veber |
| 48562 | Richard | 295739 | Matrix | science-fiction | 2/24/2011 | 03/01/11 | 19567 | Wachowski |
| 58221 | Dupont | 395743 | Sideways | romantique | 3/22/2011 | 04/10/11 | 14976 | Payne |
| 58221 | Dupont | 537656 | Le Pari | comique | 05/12/11 | 06/06/11 | 28567 | Bourdon |
La médiathèque ne gère pas plus d'un exemplaire par DVD, et un utilisateur ne peut pas emprunter un DVD plusieurs fois.
- Transformez la relation en 1NF et indiquez la clé primaire.
- Identifiez les dépendances fonctionnelles et transformez en 2NF.
- Identifiez les dépendances transitives et transformez en 3NF.
Correction détaillée
1. 1NF. Chaque attribut multivalué doit devenir monovalué : nomUtilis est répété sur plusieurs lignes pour le même utilisateur, et nomRealis de même. Il faut donc écrire une ligne par emprunt, en répétant explicitement numUtilis et nomUtilis sur chaque ligne — ce qui rend chaque ligne unique et fait apparaître la redondance au grand jour. La clé primaire est (numUtilis, codeDvd) : un utilisateur n'emprunte un DVD qu'une fois, donc ce couple identifie l'emprunt.
2. Dépendances fonctionnelles et 2NF. Les dépendances partielles sautent aux yeux :
1numUtilis -> nomUtilis2codeDvd -> titreDvd, genreDvd, numRealis3numRealis -> nomRealis4numUtilis, codeDvd -> dateEmprunt, dateRetournomUtilis ne dépend que de numUtilis ; titreDvd, genreDvd et numRealis ne dépendent que de codeDvd. Ce sont des dépendances partielles (la clé est composite) et une dépendance transitive (codeDvd → numRealis → nomRealis). Décomposition en 2NF :
1Utilisateur(numUtilis, nomUtilis)2Dvd(codeDvd, titreDvd, genreDvd, numRealis#)3Emprunt(numUtilis#, codeDvd#, dateEmprunt, dateRetour)4Realisateur(numRealis, nomRealis)3. 3NF. La dépendance transitive restante est : nomRealis ne dépend pas directement de codeDvd mais du réalisateur. En plaçant Realisateur(numRealis, nomRealis) à part — ce que fait la décomposition ci-dessus — la 3NF est atteinte. Dvd conserve numRealis comme clé étrangère, ce qui permet de reconstituer le nom du réalisateur par jointure.
La leçon de cet exercice : la 2NF et la 3NF se traitent dans l'ordre mais se diagnostiquent ensemble. Une table peut porter simultanément des dépendances partielles et une dépendance transitive ; la décomposition en 2NF peut d'ailleurs résoudre une partie du problème de 3NF, sans jamais le garantir.