Aller au contenu principal

Bases de données & SQL · L2 · Section 6/11

Normalisation

Progression

Points d’expérience : XPSérie de jours consécutifs : · —Progression du module : — / —compris

#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 → Y signifie 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 ABA \to B) 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) :

ABCDE
abzwq
ebrwp
adzwt
edrwq
afzst
efrst

Lecture : A prend deux valeurs (a, e) ; à a correspondent toujours C = z et D = w, à e correspondent toujours C = r et D = w. Donc ACA \to C et ADA \to D. En revanche a est associé à b puis à d puis à f : A↛BA \not\to B. Symétriquement, CAC \to A et CDC \to D.

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éphones avec « 06…, 07… ») et s'il porte plusieurs informations distinctes dans une seule valeur (attribut composite : une colonne BatEtEsc contenant « 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 :

texttext

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 3NF

Le 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

Détecter DF
Repérer X→Y qui violent 3NF/BCNF
Scinder R
R(XYZ) → R1(XY) + R2(XZ)
Clés & DF
Vérifier clés et dépendances dans chaque relation
Sans perte
R1 ⋈ R2 = R via clé commune ; préserver les DF

#É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 :

sqlsql

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.

Étape 1 / 4

#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.

  1. Listez les anomalies d'insertion, de mise à jour et de suppression.
  2. Donnez la décomposition en 3NF, sans perte.
  3. 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).

numPersonnelnomPfonctionsalairenumFilialeadresseF
12Blancgérant30000567 Champs-Élysée, Paris
37Bertrandassistant120003243 rue des Roses, Montréal
14Renaultsuperviseur180003243 rue des Roses, Montréal
9Thaleassistant900073 rue de la Buffa, Nice
45Brièregérant240003243 rue des Roses, Montréal
41Hetteassistant9000567 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 numFilialeadresseFnumFiliale \to adresseF, 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) :

numPraticiennomDentistenumPatientnomPatientrendezVousnumCabinet
S1011FastrezP100Blanc9/12/2012 10h00C15
S1011FastrezP105Denaux9/12/2012 12h00C15
S1024MaréchalP108Piette9/12/2012 10h00C10
S1024MaréchalP108Piette9/14/2012 14h00C10
S1032MeierP105Denaux9/14/2012 16h30C15
S1032MeierP110Marchand9/15/2012 18h00C13

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.

  1. Identifiez la clé primaire.
  2. Identifiez les dépendances fonctionnelles.
  3. Normalisez en 3NF.
  4. Ajoutez l'attribut prestationCabinet et 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 :

texttext

1numPraticien, numPatient, date -> heure, numCabinet    (et chacun pris séparément aussi)2numPraticien                   -> nomDentiste3numPatient                     -> nomPatient4numPraticien, date             -> numCabinet

La 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.

texttext

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é.

numUtilisnomUtiliscodeDvdtitreDvdgenreDvddateEmpruntdateRetournumRealisnomRealis
48562Richard185634La Chèvrecomique01/12/111/21/201114564Veber
48562Richard295739Matrixscience-fiction2/24/201103/01/1119567Wachowski
58221Dupont395743Sidewaysromantique3/22/201104/10/1114976Payne
58221Dupont537656Le Paricomique05/12/1106/06/1128567Bourdon

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.

  1. Transformez la relation en 1NF et indiquez la clé primaire.
  2. Identifiez les dépendances fonctionnelles et transformez en 2NF.
  3. 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 :

texttext

1numUtilis -> nomUtilis2codeDvd   -> titreDvd, genreDvd, numRealis3numRealis -> nomRealis4numUtilis, codeDvd -> dateEmprunt, dateRetour

nomUtilis 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 :

texttext

1Utilisateur(numUtilis, nomUtilis)2Dvd(codeDvd, titreDvd, genreDvd, numRealis#)3Emprunt(numUtilis#, codeDvd#, dateEmprunt, dateRetour)4Realisateur(numRealis, nomRealis)

3. 3NF. La dépendance transitive restante est codeDvdnumRealisnomRealiscodeDvd \to numRealis \to nomRealis : 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.

#Quiz

Quelle dépendance viole la 3NF ?
Quelle dépendance viole la 3NF ?