Thématiques principales

vendredi 9 février 2018

SGBD-R : Normalisation

Nous avons vu dans l’article introductif divers concepts sur les SGBD-R et nous sommes arrivé à la notion primordiale de normalisation de bases de données relationnelles. Voyons en quoi cela consiste.

La normalisation

La normalisation de bases de données est une manière de la modéliser, concevoir et structurer. La normalisation est même une propriété pour une base de données (selon différents niveaux) car les systèmes de gestion de bases de données sont eux même conçus en partant du principe que les bases de données qu’ils auront à gérer seront normalisées. Ainsi normaliser une base va permettre:
  • réduction des erreurs et des temps de résolutions
  • l'écriture de requête plus simple
  • une meilleur intégrité des données (absence de redondance de l’information)
  • des tris plus rapides
  • des mises à jour plus rapide
  • des accès concurrents plus simple à gérer
La normalisation se fonde sur la notion de dépendances traduisant des contraintes sur les concepts de la base de données qui doit être structurée. Elle se présente comme une suite de forme normale appliquant des contraintes et des algorithmes. Il s’agit d’un processus de réduction de la relation universelle en un ensemble de relation présentant un minimum de redondance d’intra-relation et un maximum d'indépendance inter-relation.

La relation universelle est la relation regroupant l’ensemble des concepts à développer dans la base de données.

 

Le processus de normalisation se décompose en différentes phases permettant d'accéder à différentes formes normales de la base de données allant de 1 à 6.

Pour expliquer les différentes phases de normalisation, prenons un exemple. Considérons une relation universelle traitant de la gestion de projet dans un processus Scrum: 

 
 Celle ci se résume alors par:
RU(nomRelease, nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie, numBacklog, estimation, points, typeTache, definitionReady, etatTache, nomTache) 
avec typeCeremonie {RevueSprint, Melée, Retrospective,DemarrageSprint} 
avec typeEquipier {ScrumMaster, Developpeur, ProductOwner} 
avec typeTache{Fonctionnelle, Technique} 
avec etatTache {Pret, EnCours, Fini}
Cette liste de concepts n’est probablement pas complète et pour la construction de la base de données, peut être que tous les attributs déclarer ne seront pas utile. Nous le découvrirons au fur et à mesure de l’application des Formes Normales. Pour pouvoir appliquer les formes normales il faut préalablement élaborer le graphe des dépendances fonctionnelles [2] afin de déterminer quels sont les attributs qui déterminent les autres et si il faut en définir d’autres.
Une dépendance fonctionnelle est une association entre deux ensembles X et Y d'attributs d'une même relation pour laquelle pour tout X il existe un unique Y.
On obtient alors :
nomEquipier -> typeEquipier; 
nomEquipier -> nomTache; 
nomEquipier -> nomSprint; 
nomCeremonie -> typeCeremonie; 
nomCeremonie -> dateCeremonie; 
nomCeremonie -> nomSprint; 
nomTache -> estimation; 
nomTache -> points; 
nomTache -> definitionReady; 
nomTache -> etatTache; 
nomTache -> typeTache; 
nomTache -> numBacklog; 
nomSprint -> nomRelease; 
nomSprint -> numBacklog; 
nomRelease -> numBacklog;
un coup de dot la dessus (fichier.dot)
dot -Tpng -oDependencyG.png DependencyG.dot
et on obtient l’arbre suivant: Appliquons maintenant les différentes formes normales

Première forme normale (FN1)

La première forme normale définit que toutes les relations de la base doivent posséder des attributs sémantiquement atomique, c’est a dire que ceux ci ne sont pas eux même des relations. Si l’on prend notre Relation universelle à la lettre en considérant que celle-ci ne contient que des Attributs, alors oui celle ci est FN1.

Seconde forme normale (FN2)

La seconde forme normale impose d’une part d'être en première forme normale. D’autre part, pour être en seconde forme normale, tous les attributs de relations n’appartenant pas à une clé candidate est en dépendance totale de chaque clef candidate. Bien ceci introduit deux nouveaux concepts, celui de clé candidate et celui de dépendance totale. Une clé candidate est un ensemble d’attributs d’une relation garantissant:
  • irréductibilité ; c’est a dire qu’il n'existe pas de sous ensemble de la clé garantissant l’unicité (en gros la clé est minimale dans la relation)
  • l’unicité : c’est à dire que deux entrée de la relation ne peuvent avoir la même clé candidate.
La notion de dépendance totale définit une dépendance qui n’est ni triviale, ni partiel. C’est a dire que la dépendance X -> A est dite total s’il n’existe pas Y inclu dans X tel que Y -> A

A noter que les notions de dépendance trivial et partiel définissent respectivement:
  • X -> Y est dite partiel si elle n’est pas trivial et si il existe Z inclu dans X tel que Z -> Y
  • X -> Y est dite triviale si Y est inclue dans X
Il y a quelques soucis car nous n’avons pas défini de clefs dans notre relation universelle. Mais en s’appuyant sur notre graphe des dépendances, nous allons probablement réussir en définir quelques unes, en choisissant entre autre les éléments déterminant les autres…. donnant le résultat suivant:
RU(nomRelease, nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie, numBacklog, estimation, points, typeTache, definitionReady, etatTache, nomTache)
En définissant des clefs, nous ne sommes évidemment pas en FN2. Pour le devenir, il va falloir appliquer le théorème de Heath. Celui ci défini que pour une relation R telle que s’il existe A, B et C des ensembles d’attributs et A -> B alors il R vaut la jointure des relations associants A,B et A,C. A noter que si B et C sont reliés par une dépendance fonctionnelle, alors il sera préférable (c’est le théorème de Jorma Rissanen) de décomposer R en A,B et B,C afin de conserver toutes les dépendances. Ainsi par exemple on peut extraire une relation associée aux taches
RU_sansTache(nomRelease, nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie, numBacklog,nomTache) 
Tache(nomTache,estimation, points, typeTache, definitionReady, etatTache, numBacklog)
Si on fait la jointure RU_sansTache avec Tache, l'operation nous restitue la RU initiale. Bien sur nous n'avons pas encore définie la jointure. Nous y reviendrons lorsque l’on traitera la jointure. Maintenant est ce que Tache est FN2? Oui! Mais pas RU_sansTache.
Il faut donc continuer la décomposition, à partir du graphe, nous pouvons nous intéresser a l’attribut nomRelease que nous pouvons extraire dans une relation Release:
RU_sansTache(nomRelease, nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie, numBacklog,nomTache) 
Release(nomRelease, numBacklog)
Ok pour Release mais toujours pas pour RU_sansTache qui en fait n’a pas évolué car nomRelease est une clef (ou partie) candidate qui devient étrangère et numBacklog qui est toujours déterminé par le Sprint. Du coup on va justement décomposer de façon à extraire une relation Sprint. Nous obtenons alors les relations suivantes:
RU_sansTache( nomSprint, typeEquipier, nomEquipier, typeCeremonie, nomCeremonie, dateCeremonie,nomTache) 
Sprint(nomSprint, nomRelease, numBacklog)
On va poursuivre en décomposant les Cérémonies:
RU_sansTache( nomSprint, typeEquipier, nomEquipier,nomTache) 
Ceremonie(nomCeremonie,typeCeremonie, nomSprint, dateCeremonie)
Et on va finir par renommer notre reste de Relation universelle en Equipier afin d’obtenir l’ensemble de relations suivant:
Equipier (nomEquipier, typeEquipier,nomTache, nomSprint) 
Ceremonie(nomCeremonie, typeCeremonie, nomSprint, dateCeremonie) 
Sprint(nomSprint, nomRelease, numBacklog) 
Release(nomRelease, numBacklog)
Voilà, nous avons terminé notre décomposition en 4 relations en FN2. Et on commence à poindre le schéma de notre futur base de données relationnelles.

Troisième forme normale (FN3)

La troisième forme normale impose d’une part d'être en deuxième forme normale. D’autre part pour être en FN3 il est nécessaire que tout attribut n’appartenant a aucune clef candidate de la relation ne soit dépendant que de cette clef candidate. Donc si pour une relation R, et les dépendances X -> Y et Y -> Z avec X,Y, Z des ensembles d’attributs tel que seul X est clé candidate alors la Relation contenant X, Y et Z n’est pas en forme normale. Dans notre graphe, cela reviendrait à dire qu’un élément est déterminé par à la fois une clef et un autre élément référencé lui même par cette même clef, et c'est pas bien. A noter que dans notre exemple nos relations sont FN3 sauf la relation Sprint dont l’attribut numBacklog est déterminé par nomRelease et nomSpring, nomRelease étant lui même déterminé par ce dernier. Que faire? En fait, nous avons un défaut de conception. Car un backlog est un ensemble de taches. Cependant, sémantiquement, le backlog de sprint ou celui d’une release ne sont pas les mêmes choses. Ils contiennent tous deux des Taches mais celui des sprints est déterminé au lancement du sprint et est figé une fois defini alors que celui de la release, lui, sert de tampon et de traitement des taches (par le PO voulant les priorisés). Nous n’avons pas voulu aller trop loin initialement dans la modélisation mais cette distinction est nécessaire. Pour résoudre cela, on va donc définir une nouvelle relation :
Backlog (numBacklog, typeBacklog)
avec typeBacklog = {backlogR, backLogS}
et désormais les références au backlog dans les autres relations deviennent des clefs étrangères :
Sprint(nomSprint, nomRelease, numBacklog) 
Release(nomRelease, numBacklog) 
Tache(nomTache,estimation, points, typeTache, definitionReady, etatTache, numBacklog)
A noter que maintenant par une petite analyse de notre modèle, les relation Sprint et Release ne sont plus que des relations d’associations. Nous sommes enfin FN3.

La forme normale de Boyce-Codd

Cette forme normale est une forme équivalente aux FN2 et FN3 mais en éliminant quelques redondances supplémentaires et peut donc être utilisée en tant qu’alternative aux deux autres formes normales Sa seule limite est qu’il est possible d'être FN3 sans forcément être en FNBC ce qui potentiellement peut nous faire perdre des dépendances fonctionnelles… donc en cas limites, il faut revenir aux FN2 et FN3. Ainsi, une relation R est en forme normale de Boyce-Codd si et seulement si, pour toutes les dépendances X -> A tel que X un ensemble d’attributs inclus dans R et A un attribut de R, alors soit A appartient a X (dépendance triviale) ou X est une sur clef dans R (ou dit autrement, X clef candidate de R).

Conclusions

L'application des formes normales sur les SGBDR est une démarche indispensable pour éviter les incohérences structurelles mais aussi sémantiques. Généralement, les démarches mises en œuvres s'arrêtent à la forme normale de niveau 3 ou la FNBC car elles sont jugées le plus souvent suffisantes (et généralement elles le sont). Dans un prochain article nous nous attarderons sur les formes normales 4, 5 et 6 qui prolongent un peu plus loin la recherche d’anomalies dans notre SGBD.

Références

[1] https://fr.wikipedia.org/wiki/Forme_normale_(bases_de_donn%C3%A9es_relationnelles) [2] https://fr.wikipedia.org/wiki/D%C3%A9pendance_fonctionnelle [3] http://fsmrel.developpez.com/basesrelationnelles/normalisation/

Aucun commentaire:

Enregistrer un commentaire