Normalisation de base de données

La normalisation de base de données offre un lot de principes faciles à appliquer. Ils aideront le développeur à structurer sa base de données et améliorer les performances MySql.

Normaliser une base de données revient à organiser les données de manière plus efficace, logique, grâce à des règles normales, des principes normaux (qui donnent une norme). Pour plus d’efficacité, le processus de normalisation résultera en la construction de tables et de relations entre ces tables. La normalisation de base de données offre :

  • une structure optimisée qui ne gâche pas l’espace disque (la même donnée ne devrait pas être trouvée dans plus d’une table pour des raisons évidentes : la mise à jour de plusieurs tables pour la même donnée est absurde)
  • des dépendances logiques (implique l’enregistrement des données pertinentes à l’intérieur de ou en relation à la bonne table)

Les Formes Normales

Je me concentrerai ici sur les 3 premières règles, que les spécialistes appellent formes normales. Ces règles devraient ajouter suffisamment d’efficacité à votre base de données. Gardez en mémoire qu’il est parfois préférable de ne pas appliquer une règle (et donc d’interrompre la normalisation) pour des raisons pratiques. En effet, la normalisation consiste en l’ajout de tables de relation (qui créent un lien entre d’autres tables), par exemple, qui nécessiteront un changement par le développeur de ses requêtes MySql.

Les formes normales doivent être appliquées dans l’ordre : une normalisation avancée ne peut se produire que dans le cas où les règles précédentes de normalisation ont déjà été appliquées.

Pour une meilleure compréhension, je prends l’exemple d’une base de données discographique qui enregistre les albums d’artistes célèbres.

Créer la base de données à froid sans expérience de normalisation résulterait en la création d’une seule table :

records_table :

record_ID : 1 (defini comme Primary_KEY)

artist : Fleetwood Mac est un groupe américano-britannique formé en 1967…

record_title : The pious bird of good omen

record_style : Blues, Rock, Pop

record_label : blue horizon

record_release : 1969

Avec cette table simpliste, vous comprendrez aisément que :

  • une requête SELECT ne peut obtenir de l’information à propos d’un artiste sans connaître un record_ID
  • une requête UPDATE qui veut modifier la biographie d’un artiste devra le faire dans chaque ligne de la table associée à l’artiste en question
  • une requête DELETE effacera l’information concernant l’album ainsi que l’information concernant l’artiste

Ces limitations produiront des anomalies comme la corruption de données, des problèmes de ressource MySql (à cause des sous-requêtes LIKE),… Il est donc temps de normaliser cette base et nous le ferons grâce à la théorie relationnelle.

La Théorie relationnelle

1NF => La première Forme Normale : la forme atomique

Chaque champ de table devrait avoir une valeur atomique, c’est-à-dire une seule valeur dans la colonne de votre table. Dans l’exemple ci-dessus, les champs ‘record_artist’ et ‘record_style’ contiennent plus qu’une seule valeur. L’application de la 1NF résultera en la création de 2 tables supplémentaires :

records_table :

record_ID : 1 (defini comme Primary_KEY)

record_title : The pious bird of good omen

record_release : 1969

and

artists_table :

artist_ID : 1 (defini as Primary Key)

artist_name : Fleetwood Mac

artist_bio : Fleetwood Mac est un groupe américano-britannique formé en 1967

et

styles_table (avec 3 entrées) :

style_ID : 1 (defini comme Primary Key)

style_name : blues

style_ID : 2

style_name : pop

style_ID : 3

style_name : rock

Il est maintenant facile d’ajoute run artiste sans devoir ajouter un album pour cet artiste, vous pouvez effacer un album sans pour autant effacer l’information concernant l’artiste,…

Les données sont maintenant séparées dans des tables différentes. Il faut maintenant créer les liens, relations entre ces tables. Les relations de base de données peuvent être du type suivant :

1 à 1 : un élément de la table A correspond à un élément de la table B et cet élément de la table B correspond à un seul élément de la table A

1 à N : un enregistrement de la records_table n’est associé qu’à un seul label mais ce label peut être associé à un nombre illimité d’enregistrements de la table records_table. Dans ce cas, nous ajouterons une clef étrangère à la table records table contenant le label_ID

N à N : un enregistrement de la records_table peut être associé à plus d’un artiste et un artiste de la artists_table peut être associé à plus d’un enregistrement de la records_table. Dans ce cas, nous créerons une table supplémentaire appelée records_artists_table, une table de relation qui liera la records_table avec la artists_table

Ajoutons une clef étrangère à la records_table pour la gestion des label_ID:

records_table : relation 1 à N

record_ID : 1 (defini comme Primary_KEY)

record_title : The pious bird of good omen

record_release : 1969

label_ID : 1

avec la table des labels associée :

labels_table :

label_ID : 1 (défini comme PRIMARY KEY)

label_name : Blue horizon

et créons la nouvelle table :

records_artists_table : relation N à N

record_ID : 1 (clef primaire définie sur les 2 champs)

artist_ID : 1 (clef primaire définie sur les 2 champs)

Nous pourrions définir le même genre de relation entre la table records_table et les styles de musique :

records_styles_table : relation N à N

record_ID : 1 (clef primaire définie sur les 2 champs)

style_ID : 1 (clef primaire définie sur les 2 champs)

2NF => la seconde Forme Normale : les relations aux clefs

Cette Forme Normale s’assure que les colonnes qui ne sont pas définies comme clef dépendent strictement de la clef primaire. Ainsi, dans le cas d’une clef composite (une ligne de la table est identifiée grâce au record_ID et au author_ID), toutes les colonnes non définies comme clef doivent dépendre des 2 champs, etpas seulement à l’une d’entre elles. Imaginez la structure suivante :

records_reviews table :

record_ID : 1 (defined as primary key with the following field)

author_ID : 1 (defined as primary key with the previous field)

review : This compilation album is one of my favourites…

author_website : http://www.domain.com

Dans l’exemple ci-dessus, le champ ‘author_website’ ne dépend pas strictement de la clef composite record_ID-author_ID. Il dépend uniquement de l’author_ID. Appliquer la seconde forme normale revient à déplacer le champ author_website field vers la table authors_table.

3NF => la troisième Forme Normale

Avec la troisième forme normale, chaque champ doit dépendre de la clef primaire. Ainsi, un champ qui dépend d’une autre colonne que la clef primaire viole la 3è Forme Normale. Les adresses dans une table en sont un bon exemple. Imaginez la structure suivante :
labels_table :
label_ID : 1 (primary key)
label_name : Blue Horizon
label_zip : 1770
label_city : Sherborn
label_state : MA
Dans cette table, les champs label_city et label_state dépendent strictement du champ label_zip, et pas de la clef primaire label_ID. Pour rendre cette table conforme à la 3NF, créez la zip_table :
zip_table :
zip_ID : 1770 (primary key)
zip_city : Sherborn
zip_state : MA
Puis la table des labels ressemblera à :
labels_table :
label_ID : 1 (primary key)
label_name : Blue Horizon
zip_ID : 1770
Vous pourriez encore considérer que le champ zip_state nécessite la création d’une table spécifique :
states_table :
state_ID : 1 (primary key)
state_name : MA
La normalisation telle qu’exposée plus haut vous permet de créer des structures de base de données propres mais implique aussi plus de complexité dans votre application. En effet, vous devrez changer vos requêtes à la base de données. Votre application reposera sur plus de table, vous aurez donc à joindre les tables dans vos requêtes, ce qui résultera en une perte de performance dans vos applications.
Je conseillerai aux développeurs un passage obligé par la normalisation en début de projet,et d’arrêter la normalisation de leurs tables juste avant que la structure de ces tables devienne trop compliquée. Dans notre exemple, extraire une ligne de la table labels résulte en la création de la requête suivante :
SELECT * FROM labels as A INNER JOIN zip as B ON B.zip_ID=A.zip_ID INNER JOIN states as C ON C.state_ID=B.state_ID WHERE A.label_ID=’1′ LIMIT 1;

Webliographie :

Tutoriel sur la normalisation par Olivier Dahan

Normalisation Mysql (English)