
Dans les chapitres précédents, je vous ai présenté les principales notions de l'approche orientée objet et la démarche globale pour modéliser une base de données.
Je vais maintenant vous montrer comment optimiser votre modèle relationnel en utilisant les formes normales.
Les formes normales sont en quelque sorte des niveaux de qualité d'un modèle relationnel qui définissent les règles que celui-ci doit respecter. Elles permettent de vérifier la robustesse de la conception du modèle en évitant la redondance des données et les problèmes de mise à jour et de cohérence qui en découlent.
Il y a 6 niveaux de formes normales, et 2 niveaux complémentaires. Nous allons nous concentrer sur les 3 premiers niveaux qui, s'ils sont respectés, apportent déjà une bonne cohérence d'ensemble.
1FN : première forme normale
Pour être en première forme normale (1FN), il faut que chaque attribut soit atomique. En d'autres termes, aucun attribut ne doit être multivalué (liste de valeurs) ou composé (si on le décompose, on obtient des informations supplémentaires).
Voici un exemple de table personne
ne respectant pas la 1FN :
id | pseudo | nom | |
---|---|---|---|
1 | Ed' la poignée | M. Édouard Bracame | edouard.bracame@joe-bar-team.org, ed@jbt.org |
2 | Joe l'arsouille | M. Jean Manchzeck | jean.manchzeck@joe-bar-team.org |
Dans cette table :
si une personne a plusieurs adresses email, alors elles sont notées dans l'attribut
email
, séparées par des virgules.l'attribut nom contient le nom mais aussi le prénom et la civilité de la personne.
Nous pourrions passer en 1FN en modifiant la table ainsi :
id | pseudo | civilite | prenom | nom | email_2 | |
---|---|---|---|---|---|---|
1 | Ed' la poignée | M. | Édouard | Bracame | edouard.bracame@joe-bar-team.org | ed@jbt.org |
2 | Joe l'arsouille | M. | Jean | Manchzeck | jean.manchzeck@joe-bar-team.org |
|
Cependant, que fait-on si une personne a 3, 10... 50 adresses email ?
On ne va pas créer 50 colonnes email au cas où ! Et si un jour une personne en a 51 on va être obligé de modifier le schéma de la base de données.
Nous allons donc transformer les attributs multivalués en une table séparée, liée à la table d'origine par une relation de type un à plusieurs


2FN : deuxième forme normale
La deuxième forme normale (2FN) ne concerne que les tables avec une clé primaire composite.
Pour être en deuxième forme normale (2FN), il faut déjà être en 1FN et en plus respecter la règle suivante : aucun attribut ne faisant pas partie de la clé primaire ne doit dépendre que d'une partie de la clé primaire.
Prenez la table resultat
donnant une note artistique à des skaters dans leur réalisation de figures :
skater [PK] | figure [PK] | note |
---|---|---|
Richie | Ollie | 8 |
Richie | Flip | 10 |
Richie | Hard-Flip | 9 |
Nyjah | Flip | 7 |
Nyjah | Hard-Flip | 8 |
Vous ajoutez une information donnant la difficulté de la figure :
skater [PK] | figure [PK] | difficulte | note |
---|---|---|---|
Richie | Ollie | 2 | 8 |
Richie | Flip | 5 | 10 |
Richie | Hard-Flip | 10 | 9 |
Nyjah | Flip | 5 | 7 |
Nyjah | Hard-Flip | 10 | 8 |
La difficulté d'une figure ne dépend que de la figure et non du skater. L'attribut difficulte
ne dépend que d'une partie de la clé primaire (figure)
et non de la clé primaire complète (skater, figure)
: vous ne respectez donc pas la 2FN.
La solution consiste donc à isoler les attributs concernés dans des tables dédiées. Je vais créer ici une table figure
et déplacer la colonne difficulte
dans cette nouvelle table :
nom [PK] | difficulte |
---|---|
Ollie | 2 |
Flip | 5 |
Hard-Flip | 10 |
Dans la table resultat
, la colonne figure
devient clé étrangère :
skater [PK] | figure_nom [PK, FK] | note |
---|---|---|
Richie | Ollie | 8 |
Richie | Flip | 10 |
Richie | Hard-Flip | 9 |
Nyjah | Flip | 7 |
Nyjah | Hard-Flip | 8 |

3FN : Troisième forme normale
La troisième forme normale (3FN) ressemble à la deuxième (2FN) mais concerne la dépendance entre attributs non clés.
Pour être en troisième forme normale (3FN), il faut déjà être en 2FN et en plus respecter la règle suivante : aucun attribut ne faisant pas partie de la clé primaire ne doit dépendre d'une partie des autres attributs ne faisant pas non plus partie de la clé primaire.
Regardez la table personne
ci-dessous :
id [PK] | civilite | nom | prenom | sexe |
---|---|---|---|---|
1 | Madame | Germain | Sophie | F |
2 | Monsieur | Hilbert | David | M |
3 | Madame | Noether | Emmy | F |
4 | Madame | Mirzakhani | Maryam | F |
5 | Monsieur | Poincaré | Henri | M |
6 | Monsieur | Villani | Cédric | M |
Cette table ne respecte pas la 3FN car l'attribut sexe
peut être déduit de l'attribut non clé primaire civilite
.
Afin de résoudre ce problème, comme pour la 2FN, il convient de créer une nouvelle table civilite
et d'y déplacer l'attribut sexe
:

Et voici le nouveau contenu des tables personne
et civilite
:
civilite [PK] | sexe |
---|---|
Madame | F |
Monsieur | M |
id [PK] | civilite [FK] | nom | prenom |
---|---|---|---|
1 | Madame | Germain | Sophie |
2 | Monsieur | Hilbert | David |
3 | Madame | Noether | Emmy |
4 | Madame | Mirzakhani | Maryam |
5 | Monsieur | Poincaré | Henri |
6 | Monsieur | Villani | Cédric |
Un autre exemple de non respect de la 3FN est illustré par la table ligne_facture
ci-dessous :
facture_id [PK] | article_id [PK] | quantite | prix_unitaire | total |
---|---|---|---|---|
1 | 1 | 3 | 10.5 | 31.5 |
1 | 13 | 10 | 9.15 | 91.50 |
1 | 42 | 8 | 4 | 32.0 |
2 | 3 | 1 | 19.99 | 19.99 |
Nous avons la colonne total qui peut être calculée avec une opération mathématique simple à partir des colonnes quantite
et prix_unitaire
(total = quantite x prix_unitaire
). Sa valeur ne dépend pas de la clé primaire.
La solution consiste à simplement supprimer la colonne total
.
Dénormalisez consciemment !
Le respect absolu de la 3FN n'est pas obligatoire. Pour diverses raisons, il peut être intéressant de « dénormaliser » un modèle relationnel.

Prenons l'exemple de notre table contenant les factures. Même s'il est possible de recalculer les montants totaux hors taxes et TTC à partir des lignes de commandes de l'expédition associée à une facture, il peut être judicieux d'ajouter les colonnes total_ht
et total_ttc
dans notre table facture
. Cela permettrait de faire des recherches par montant total plus facilement mais aussi de pouvoir purger les tables expedition
, ligne_commande
et commande
à intervalles réguliers sans perdre l'information des montants totaux dans les factures.