Cet après-midi, en me penchant sur la modélisation de la base de données de mon jeu (en développement, je vous en parlerai plus tard 🙂 ), je me suis demandé jusqu’où aller sur la normalisation des données. Si vous ne savez pas ce que c’est, je vais vous en parler brièvement.

La normalisation des bases de données

Qu’est-ce que la normalisation des données ? J’en ai une définition très simpliste, mais qui la résume assez bien : Il s’agit de structurer et mettre en place des règles sur la construction d’une base de données afin de respecter une certaine cohérence des données et éviter tout superflu.

Il existe plusieurs règles (appelées formes normales) afin de normaliser une base de données. Il en existe 8 au total, dont une est très peu utilisée. Pour ma part, je vais vous présenter les 3 que je considère comme fondamentales et nécessaires à la compréhension de cet article.

Première Forme Normale (1FN)

Chaque relation doit être constituée de données atomiques, ce qui signifie que toute intersection de lignes (enregistrements) et de colonnes (champs) ne doit contenir qu’une seule donnée non segmentable. De plus, cette donnée se doit d’être constante (on mettra une date de naissance plutôt que l’âge de la personne par exemple).

Ex : Un champ Nom qui contiendrait la valeur « Max Koder » ne respecte pas la 1FN. Il faut alors découper en 2 champs : Prénom + Nom

L’utilisation de la 1FN rend la recherche dans les champs beaucoup plus aisée et rapide.

Seconde Forme Normale (2FN)

Pour être 2FN, une relation doit d’abord respecter la première forme normale. Ensuite, il faut que chaque attribut non primaire dépende de la clé primaire ou d’une clé candidate toute entière , et non pas une partie.

Prenons cette table, qui représente les expéditions à traiter suite aux commandes passées sur un site :

On remarque la clé primaire définie sur 2 colonnes : numero_client et numero_article

Identifions les DF (dépendances fonctionnelles) :

  • qte_article dépend bien du numero_client (celui qui a passé la commande) et du numéro d’article.
  • en revanche, adresse_client ne dépend que du numéro de client, pas des articles commandés.

En conclusion, cette relation n’est pas 2FN, car l’adresse du client ne dépend que d’une partie de la clé, et non la totalité.
Pour palier à cela, il faudrait sortir l’adresse du client et la basculer dans une autre table dédiée au client.

Dans l’exemple plus haut, que se passe t-il si un client passe plusieurs commandes ? Son adresse apparait plusieurs fois dans la table, inutilement.

Le respect de la 2FN tend donc à limiter la redondance des données.

Troisième Forme Normale (3FN)

Pour être considérée comme 3FN, une relation doit d’abord respecter la 2FN, mais aussi ne posséder que des attributs non-clé qui dépendent directement et uniquement de la clé.
En d’autres mots, tous les champs qui ne font pas partie de la clé (primaire ou candidate) dépendent de cette clé.

Pour être original, prenons une table Books :

normal1

Ici, la clé primaire est name.

Identifions les DF :

  • page_number, qui correspond au nombre de pages, dépend directement du nom du livre
  • author dépend également du livre en question
  • En revanche, author_adress dépend de author et de la clé primaire name. Notre relation n’est donc pas 3FN.

Voilà comment il faudrait modéliser cela :

normal2

 

Ici, chaque attribut ne dépend que de la clé.

Comme pour la 2FN, le non-respect de la 3FN entraine une redondance des données rendant ainsi les requêtes à une table plus longues et plus lourdes.

En règle générale, les formes normales visent à garantir une certaine cohérence des données et limiter les redondances de données. Ainsi, on remarque que la décomposition d’une relation complexe en 2 relations basiques peut-être la solution la plus raisonnable :

Dans notre précèdent exemple, si nous avions 1000 livres écrits par seulement 50 auteurs, en ne respectant pas la 3FN, nous aurions 1000 enregistrement * 4 champs = 4000 données.
En revanche, dans notre table qui respecte la 3FN (où l’adresse de l’auteur est sortie), nous avons :

  • Dans la table Books : 1000 enregistrements * 3 champs = 3000 données.
  • Dans la table Authors : 50 enregistrements (car 50 auteurs) * 2 champs = 100 données.

Au total, 3100 informations à traiter. Convaincu ? 😉

Cependant, gardez en tête que ces 3 formes normales ne sont que le début de la normalisation et qu’elles n’évitent pas toutes formes de redondance.

La structure par la définition

Je vais maintenant essayer de vous présenter une petite technique très simple mais qui fait beaucoup dans la conception d’une table, avant même de parler de formes normales.
Pour chaque table à créer, vous devriez pouvoir la définir en une simple phrase.
Par exemple :

Cette table présente tous les livres présents dans une bibliothèque.

Cependant, il ne doit y avoir aucun ‘mais’, ni ‘si’, et par dessus tout, aucun ‘peut-être’. En effet, toute forme de négation ou de condition est à proscrire dans cette définition. Dans ce cas, il faut réécrire votre table.
À ne pas faire par exemple :

Cette table représente tous les livres présents dans la bibliothèque, mais si le champ present est à 2 alors il est absent.

Ici la définition n’est pas bonne, puisque tous les livres sont censés être présents. S’il doit y avoir des livres absents, on les basculera dans une autre table, livres_percus par exemple.

Un second exemple, avec une table qui affiche les clients d’une bibliothèque et tous les livres qu’ils ont empruntés :

normal4

Comment décririez-vous cette table ? Avec des ‘peut-être que le client a pris 1 livre, ou 2, …‘.

Ici, la table présente 3 points négatifs :

  • Comment savoir si le client ne prendra pas plus de 6 livres ? A combien de livres met-on la limite ?
  • Si quelques clients prendront plusieurs livres, beaucoup n’en prendront qu’un ou 2. Ainsi, vous allez vous retrouver avec une multitude de champs vides, ce qui allongera inutilement les temps de traitement.
  • Enfin, tous ces champs vont être bien plus compliqués à gérer qu’une table dédiée. Par exemple, comment effectuer une recherche afin de savoir si un livre donné à déjà été prêté ?

Bien qu’elle ne remplace pas l’emploi des formes normales, appliquer cette simple méthode vous permettra de dégrossir simplement le travail de conception.

Une nécessité absolue ?

Nous avons vu que les formes normales imposaient des contraintes assez fortes. Mais faut-il les appliquer à la lettre absolument dans tous les cas ?

Prenons une table simple où l’on enregistre les visiteurs d’un site avec leur adresse IP et le navigateur utilisé.

On pourrait présenter cette table avec 2 champs : navigateur et adresse IP. Cependant la 1FN nous dicte de stocker des données atomisées. Découpons donc l’adresse IP en 4 champs, puis pour le navigateur, un champ navigateur et un champ version :

normal5

Voilà, la 1FN est à présent respectée. Qu’est ce que cela apporte ?

L’intérêt de découper ces 2 attributs ici est clairement de pouvoir effectuer des recherches plus facilement, comme trouver toutes les IP qui appartiennent à la classe C, ou trouver des visiteurs qui utilisent des vieilles versions de navigateurs.

Soit. Mais est-ce réellement utile ? Avez-vous systématiquement besoin de faire des recherches aussi poussées ?
Selon moi, c’est à vous de vous poser les bonnes questions et de ne pas tomber dans l’atomisation extrême inutile.

Le mot de la fin

Comme je vous l’ai dit, ceci n’est que le début de la normalisation des bases de données. Je pense faire une suite à cet article dans quelques temps, mais en attendant rien ne vous empêche de faire quelques recherches sur ces fameuses formes normales 🙂