dimanche 11 octobre 2009

Rappels sur la normalisation

Qu'est-ce que la normalisation ?

Ce que l'on appelle normalisation (ou "formes normales") en matière de bases de données relationnelles est un ensemble de règles à respecter pour préserver l'intégrité des données (c'est-à-dire la cohérence des relations) tout en limitant les redondances (stockage répété de valeurs identiques).

On pourrait faire le parallèle entre les formes normales et les design patterns : dans un cas comme dans l'autre, il s'agit de décrire une solution éprouvée à un problème courant. Tous deux permettent de guider la conception (soit de la base, soit d'un programme ou d'une partie d'un programme), laissant au développeur le soin de l'implémentation.

Outre les deux objectifs principaux cités plus haut (préservation de l'intégrité des données et prévention de la redondance), les formes normales offrent aussi pour avantage, lorsqu'elles sont respectées avec scrupule (mais discernement, nous en reparlerons plus tard) de faciliter la maintenance et la collaboration. En effet, en normalisant ses bases de données, on s'assure que quiconque connaissant les formes normalisées saura les reconnaitre, et comprendre d'autant plus aisément l'organisation des données.

Les principales formes normales

Une fois n'est pas coutume, ceux qui ne connaissent pas la normalisation vont s'apercevoir que dans les faits, ils la pratiquent déjà, comme la prose de M. Jourdain (on ne se lasse pas de cette référence !). Ceci pour une simple raison : comme beaucoup de bonnes pratiques, la normalisation a été tout d'abord édictée par le bon sens.

Voici une brève présentation des 3 premières formes normales :
  • 1FN (1ère forme normale)
    • La plus élémentaire de toutes, la première forme normale impose que toute donnée soit indivisible (atomique), que la table dispose d'une clé primaire, composée d'une ou plusieurs colonne, et que les données redondantes soient extraites dans leur propre table, disposant d'une clé primaire elle-aussi.
      • Exemple
        • employes(secu, date_embauche, nom, prenom)
      • Contre-exemple
        • employes(secu, ancienneté, nom), où nom servirait à stocker à la fois le nom et le prénom, et poste la définition du poste de l'employé.
    • Concrètement, le respect de la première forme normale garantit la possibilité de filtrer, trier et/ou lier une table sur n'importe laquelle de ses colonnes. Dans le contre-exemple, il serait impossible de sélectionner toues les employés portant le même nom sans recourir à un traitement sur la colonne (ex. nom LIKE 'nom_de_famille%'), ce qui n'est ni des plus performants, ni des plus pertinents (imaginez avoir un employé nommé 'Martin' et un autre nommé 'Martinot'...). 
    • Remarquez également que l'on aura préféré stocker la date d'entrée de l'employé dans l'entreprise plutôt que son ancienneté exprimée en durée, tout simplement parce que dans ce dernier cas, une mise-à-jour régulière de l'enregistrement est nécessaire pour  maintenir l'exactitude de cette donnée. Ceci constitue une autre règle essentielle de normalisation.

  • 2FN (2ème forme normale)
    • La respect de la deuxième forme normale implique tout d'abord le respect de la 1ère. Cette règle est valable pour chaque règle ultérieure. La 2FN s'applique aux tables employant des clés composites. Elle prescrit que toutes les données ne faisant pas partie de la clé doivent en dépendre directement (mais pas d'une partie seulement de la clé).
      • Exemple : 
        • fonctions(departement, poste)
        • emplacement(departement, batiment)
      • Contre-exemple :
        • fonctions(departement, poste, batiment)
    • En admettant que dans cette société, chaque département se voit attribuer un et un seul bâtiment, respecter la 2NF a entraîné la scission de la table en deux, car l'attribut batiment ne dépendait que d'une partie de la clé (departement) et non pas de la totalité.

  • 3FN (3ème forme normale)
    • La troisième forme normalisée impose que chaque attribut qui n'est pas une clé dépende de la  clé primaire.
      • Exemple : 
        • employes(secu, nom, date_embauche, prenom, poste)
        • salaires(poste, ancienneté, salaire)
      • Contre-exemple :
        • employes(secu, nom, date_embauche, prenom, poste, salaire)
    • Ici, on considère que le salaire dépend de l'ancienneté (déduite de la date d'embauche) et du psote occupé. Dans ce cas, pour respecter la 3NF, il nous faut séparer l'information salaire de la table employes car cette information ne dépend pas de la clé (secu) mais d'autres attributs  (date_embauche et poste) qui ne font pas partie de la clé primaire.

Appliquer ces règles de conception à toutes ses tables est le plus souvent une bonne idée. S'il existe d'autres formes normalisées un peu plus complexes, le respect de ses trois là permet déjà de s'assurer d'un maximum de cohérence pour un minimum de redondance, le tout selon une façon standardisée de faire qui devrait grandement faciliter la compréhension de tous les intervenants.

Bien sûr, on peut redouter que le fait de multiplier le nombre de tables peut avoir un impact négatif sur les performances globales de l'application, et complexifier légèrement l'écriture des requêtes. Si cette observation n'est pas sans fondement, on s'aperçoit souvent à l'usage que des tables designées de façon anarchiques ne sont guère plus performantes, et que les requêtes à écrire pour les interroger sont d'autant plus complexes qu'elles ne répondent pas à des schémas standards. Autrement dit, quelle que soit la façon d'aborder le problème, la conception de bases de données reste un exercice délicat, et dans la plupart des cas, même si l'on est pas convaincu d'emblée, il est préférable de s'en remettre aux règles de normalisation pour éviter de commettre des erreurs dont les conséquences peuvent s'avérer colossales selon l'ampleur des projets !