Normalisation

Normalisation

Normalisation est le processus de restructuration de nos entités et leurs attributs. Il y a plusieurs objectifs :

  • la minimisation de redondance dans notre schĂ©ma

  • la dĂ©tection et rĂ©solution des problĂšmes de consistance

  • ...

Nous faisons évoluer notre schéma de façon itérative, en passant par plusieurs phases de normalisation :

  • 1NF – First normal form

  • 2NF – Second normal form

  • 3NF – Third normal form

  • BCNF – Boyce-Cobbs normal form

Normalisation

1NF

Une entitĂ© est en 1NF s’il n’y a pas d’attribut composĂ© ou multivaleur

Par exemple :

étudiant(id, nom, prénom, adresse)

L'entitĂ© Ă©tudiant n'est pas en 1NF parce que l'adresse peut ĂȘtre considĂ©rĂ©e comme « composĂ© » de numĂ©ro, rue, city, dĂ©partement, rĂ©gion, pays, code postal.

Nous faisons évoluer le schéma de l'étudiant :

étudiant(id, nom, prénom, addr_numéro, addr_rue, addr_city, addr_dept, addr_region, addr_pays, addr_code)

Un autre exemple. On a une entité qui représente nos étudiants et leurs cours.

étudiant(numéro, nom, cours)
numéro
nom
cours

1

Bonnetain

[ SGBDR, Backend ]

2

Laurendeau

[ Angular ]

3

Perrousset

[ Backend, Angular ]

On décide de lister les cours de chaque utilisateur dans un seul champ cours. Le type de l'attribut cours est un array des noms des cours. Donc, ici on a un problÚme de domaine.

Certes, on utilise un format structuré (pseudo-json). Cette table n'est pas en 1NF. La colonne cours est multi-valeur. Dans ce cas, il serait difficile d'éxtraire tous les étudiants du cours Backend, par exemple.

Il faut modifier le type (et donc restreindre le domaine) de l'attribut cours : on l'imite à un seul cours. Nos données se transforment donc ainsi :

numéro
nom
cours

1

Bonnetain

SGBDR

1

Bonnetain

Backend

2

Laurendeau

Angular

3

Perrousset

Backend

3

Perrousset

Angular

Cette table est maintenant en 1NF.

2NF

Une entité est en 2NF si :

  • elle est dĂ©jĂ  en 1NF

  • elle n’a pas de dĂ©pendance partielle, c'est-Ă -dire il n’y a pas d’attribut non-prime qui dĂ©pend d’un sous-set d’une clĂ© candidate. En langage plus simple : il ne faut pas qu'il y a des attributs qui dĂ©pendent entiĂšrement sur un sous-set de la clĂ© primaire.

Exemple, on aimerait afficher le tarif des cours des étudiants :

étudiant(numéro, nom, cours, prix)

En échantillon des données possibles est :

numéro
nom
cours
prix

1

Bonnetain

SGBDR

1000

1

Bonnetain

Backend

1500

2

Laurendeau

Angular

2000

3

Perrousset

Backend

1500

3

Perrousset

Angular

2000

Ici, le prix dĂ©pend du cours, donc il y a une dĂ©pendance fonctionnelle cours ➔ prix.

  • La seule clĂ© candidate est { numĂ©ro, cours }

  • prix est donc un attribut non-prime (il ne fait pas partie de la clĂ© candidate)

  • prix dĂ©pend d’un sous-set de la clĂ© candidate (cours), et donc il y a une dĂ©pendance partielle

  • alors cette table n’est pas en 2NF

En réalité, il est plutÎt intuitif que cette représentation n'est pas idéal. Il suffit de se poser la question : "et si demain le prix d'un cours change ?". La réponse, je serais obligé de modifier beaucoup de lignes, alors que la modification devrait impacter une seule ligne.

Quelle est la solution ? Il faut couper notre entité en deux.

étudiant(numéro, nom, cours)
cours(cours, prix)

Exemple de la table étudiant :

numéro
nom
cours

1

Bonnetain

SGBDR

1

Bonnetain

Backend

2

Laurendeau

Angular

3

Perrousset

Backend

3

Perrousset

Angular

Exemple de la table cours :

cours
prix

SGBDR

1000

Backend

1500

Angular

2000

Est-ce que nos entités sont tous en 2NF ? Non ! Il existe encore une dépendance partielle !

  • l'attribut nom (non-prime) dĂ©pend de numĂ©ro, et numĂ©ro fait partie de notre clĂ©-candidate { numĂ©ro, cours }

Il faut encore couper en deux les entités :

étudiant(numéro, nom)
étudiant_cours(numéro, cours)
cours(cours, prix)

Exemple de la table étudiant :

numéro
nom

1

Bonnetain

2

Laurendeau

3

Perrousset

Exemple de la table étudiant_cours :

numéro
cours

1

SGBDR

1

Backend

2

Angular

3

Backend

3

Angular

Exemple de la table cours :

cours
prix

SGBDR

1000

Backend

1500

Angular

2000

3NF

Une entité est en 3NF si :

  • elle est en 2NF

  • Il n’y a pas de dĂ©pendance transitive pour les attributs non-primes

  • Un attribut non-prime ne peut pas ĂȘtre dĂ©pendant d’un autre attribut non-prime

Qu'est-ce que c'est une dépendance transitive ?

Dans un set de relations fonctionnelles :

{ 
  A ➔ B
  B ➔ C 
} 

Une dĂ©pendance transitive existe : A ➔ C

Exemple d'une entité étudiant :

étudiant(id, nom, prénom, téléphone, département, région, pays, ùge)
  • elle est en 1NF, car il n'y a pas d'attribut composĂ© ou multivaleur

  • elle est en 2NF parce qu'il n'y a pas de dĂ©pendance partielle (la clĂ© candidate est id uniquement)

  • elle n'est pas en 3NF parce qu'il existe une dĂ©pendance transitive !

Pourquoi ?

Parmi les dépendances fonctionnelles, on trouve :

id ➔ dĂ©partement
dĂ©partement ➔ rĂ©gion
rĂ©gion ➔ pays

Il y a donc une dépendance transitive entre id et pays !

La solution, encore une fois, est de couper l'entité en deux :

étudiant(id, nom, prénom, téléphone, département, région, ùge)
région_pays(région, pays)

Est-ce qu'on a atteint la 3NF ? Pas encore ! Il y a toujours des dépendances transitives à résoudre :

étudiant(id, nom, prénom, téléphone, département, ùge)
département_région(département, région)
région_pays(région, pays)

Au-delĂ  de 3NF

D’autres formes normalisĂ©es qui ajoutent de contraintes sur les donnĂ©es :

  • BCNF : boyce-codd normal form (une forme plus restreinte de 3NF)

  • 4NF

  • 5NF

  • etc..

Je vous laisse étudier ces formes plus strictes, par exemple ici

Dénormalisation

Plus on normalise, plus on serait obligé de faire les jointures de table pour récupérer les données :

  • RequĂȘtes SQL complexes

  • ExĂ©cution plus lente

En plus, le passage en BCNF pourrait aussi casser des relations fonctionnelles.

Le processus de dĂ©-normalisation introduit quelques donnĂ©es redondantes dans l’intĂ©rĂȘt de performance.

Exemple :

user(user_id, email, nom, prénom)
post(post_id, user_id, contenu, date)
post_commentaire(commentaire_id, post_id, contenu, date)

Si on voulait rĂ©cupĂ©rer tous les commentaires de tous les post d’un utilisateur, nous serions obligĂ©s de faire un join entre au moins post et post_commentaire

Si c’est une requĂȘte qu’on fait souvent, on pourrait optimiser en dĂ©normalisant, on ajoute user_id Ă  la table post_commentaire

post_commentaire(commentaire_id, post_id, user_id, contenu, date)

Maintenant, on peut simplement faire un select simple en filtrant par le user_id.

Observations

Un schéma n'est pas statique, il pourrait changer au fur et à mesure :

  • le schĂ©ma Ă©volue avec notre comprĂ©hension du problĂšme ou du domaine.

    • Parfois, c'est aprĂšs la mise en service de notre plateforme qu'on se rend compte !

  • avec l’évolution de notre logique business.

    • Les entreprises (surtout les startups) peuvent pivoter, changer de fonction. Il faut ĂȘtre prĂȘt Ă  s'adapter en fonction !

Exercice

(basé sur une vraie startup auquel j'ai eu le plaisir de participer)

Vous lancez une appli qui va permettre aux utilisateurs de garder un journal des repas. À chaque repas, l’utilisateur ouvre l’appli et prĂ©cise des informations suivantes :

  • Quel repas (petit-dej, dej, etc)

  • Les aliments du repas

  • Photos

  • Les sensations pendant ou aprĂšs le repas (satisfaction, toujours faim, etc)

  • Un texte libre permettant de dĂ©crire d’autres infos concernant le repas

Le but de cette appli est de pouvoir aider aux gens, et leurs nutritionnistes, de comprendre plus précisément leur consommation :

  • Le profile des macro-aliments (glucides, gras, protĂ©ines) par repas et par jour

  • La totale calorifique par repas et par jour

  • Les sensations qui permettraient aux nutritionnistes et/ou psychologues d’adapter les rĂ©gimes alimentaires qui amĂ©liorent la vie quotidienne (et donc l’adhĂ©rence) de l’utilisateur

Utilisez les techniques de modélisation (entités, normalisation, etc) afin de modéliser une base de données pour cette application.

Le livrable est un schéma E-R (vous pouvez utiliser un des outils gratuits pour le produire)

Attention Ă  rĂ©duire autant possible la redondance ou risque d’erreurs.

DerniĂšre mise Ă  jour