La Poste
Des ingénieurs de La Poste ont conçu un MCD en UML dans le but de créer une base de données leur permettant de recueillir des statistiques sur les courriers acheminés.
Pour chaque courrier, on enregistre l'adresse (rue et numéro uniquement) du destinataire (obligatoire) et de l'émetteur (facultative) ainsi que le type (lettre ou colis). Un suivi peut être effectué pour savoir quand le courrier a été envoyé et reçu.
Chaque adresse est gérée par un bureau de poste identifié par un code postal. Les bureaux de poste sont reliés à des centres de tri qui jouent le rôle d'intermédiaire dans l'acheminement des courriers entre bureaux de poste. Un acheminement entre un centre de tri et un bureau de poste est réalisé, dans les deux sens possibles, par un ou plusieurs types de transport (voiture, train, avion...). À chaque type de transport correspond une valeur de taxe carbone.
Question
Traduisez ce MCD en langage relationnel, en spécifiant les clés (candidates, primaires et étrangères) ainsi que les contraintes (unicité, non-nullité, etc.). Il n'est pas demandé de préciser le type des attributs.
Justifiez les choix que vous faites. Faites des hypothèses si nécessaire.
Solution
Première partie du MLD
BureauDePoste(#codePostal)
Adresse(#codePostal=>BureauDePoste.codePostal, #num, #rue)
CentreDeTri(#ville)
Acheminement(#centreDeTri=>CentreDeTri.ville, #bureauDePoste=>BureauDePoste.codePostal, #transport, taxeCarbone)
Contrainte : Proj(CentreDeTri, ville) IN Proj(Acheminement, centreDeTri) et Proj(BureauDePoste, codePostal) IN Proj(Acheminement, bureauDePoste)
Remarque : Choix de la transformation de l'héritage
Les différentes informations du MCD sont :
classe mère abstraite ;
héritage exclusif ;
héritage presque complet.
Les deux premières informations sont en faveur de la transformation par les classes filles. Les deux dernières sont en faveur de la transformation par la classe mère.
La transformation par référence reste une solution possible, mais est moins avantageuse puisqu'il faudra contrôler :
qu'aucune instance de
Courrier
ne soit référencée ni par une instance deLettre
, ni par une instance deColis
(classe mère abstraite) ;qu'aucune instance de
Courrier
ne soit simultanément référencée par une instance deLettre
et deColis
(héritage exclusif).
Transformation par la classe mère
Courrier(#id, numSuivi, dateEmission, dateRéception, type:{lettre|colis}, timbreVert, poids, émetteurCodePostal=>Adresse.codePostal, émetteurNum=>Adresse.num, émetteurRue=>Adresse.rue, destinataireCodePostal=>Adresse.codePostal, destinataireNum=>Adresse.num, destinataireRue=>Adresse.rue) avec numSuivi unique et type, destinataireCodePostal, destinataireNum et destinataireRue non nuls
vLettre=Projection(Restriction(Courrier, type='lettre'), id, timbreVert)
vColis=Projection(Restriction(Courrier, type='colis'), id, poids)
Chaque enregistrement aura une valeur nulle, soit pour timbreVert
soit pour poids
.
Le fait qu'on ait un héritage exclusif et une classe mère abstraite limite le nombre de valeurs possibles pour type
.
Transformation par les classes filles
Lettre(#id, numSuivi, dateEmission, dateRéception, timbreVert, émetteurCodePostal=>Adresse.codePostal, émetteurNum=>Adresse.num, émetteurRue=>Adresse.rue, destinataireCodePostal=>Adresse.codePostal, destinataireNum=>Adresse.num, destinataireRue=>Adresse.rue) avec numSuivi unique et destinataireCodePostal, destinataireNum et destinataireRue non nuls
Colis(#id, numSuivi, dateEmission, dateRéception, poids, émetteurCodePostal=>Adresse.codePostal, émetteurNum=>Adresse.num, émetteurRue=>Adresse.rue, destinataireCodePostal=>Adresse.codePostal, destinataireNum=>Adresse.num, destinataireRue=>Adresse.rue) avec numSuivi unique et destinataireCodePostal, destinataireNum et destinataireRue non nuls
Cette transformation est un peu plus lourde à mettre en œuvre puisqu'il faut répéter dans chaque classe fille toutes les clés étrangères issues des associations de la classe mère (avec Adresse
et Suivi
).
Transformation par référence
Courrier(#id, numSuivi, dateEmission, dateRéception, émetteurCodePostal=>Adresse.codePostal, émetteurNum=>Adresse.num, émetteurRue=>Adresse.rue, destinataireCodePostal=>Adresse.codePostal, destinataireNum=>Adresse.num, destinataireRue=>Adresse.rue) avec numSuivi unique et destinataireCodePostal, destinataireNum et destinataireRue non nuls
Lettre(#id=>Courrier.id, timbreVert)
Colis(#id=>Courrier.id, poids)
vLettre=Jointure(Lettre, Courrier, Lettre.id=Courrier.id)
vColis=Jointure(Colis, Courrier, Colis.id=Courrier.id)
Contraintes : Proj(Courrier, id) IN (PROJ(Lettre, id) UNION Proj(Colis, id)) et PROJ(Lettre, id) INTER Proj(Colis, id) = {} (ensemble vide)
La contrainte à implémenter rend cette transformation également plus lourde à mettre en œuvre.
Remarque : Choix de la transformation de la relation 0..1:1
Dans les trois variantes précédentes, la classe Suivi
a été absorbée dans Courrier
ou Lettre
et Colis
(selon la transformation de l'héritage). Cela semble correct d'un point de vue sémantique puisqu'un suivi ne peut concerner qu'un courrier. Il est à noter que numSuivi
perd ici son caractère de clé, puisqu'il peut être nul, mais reste unique.
Il aurait également été possible d'avoir à chaque fois une relation distincte pour Suivi
. Dans le cas où on a une relation pour la classe mère Courrier
, cela donne :
Suivi(#numSuivi, dateEmission, dateRéception, courrier=>Courrier.id) avec courrier clé candidate
En revanche, si on n'a des relations que pour les classes filles Lettre
et Colis
, on devrait avoir dans Suivi
deux clés étrangères lettre
et colis
, toutes deux clés candidates de la relation, mais ne pouvant être valuées simultanément, ce qui est impossible. Une solution consiste à intégrer une clé étrangère suivi
dans Lettre
et Colis
, mais il faut alors interdire toute instance de Suivi
d'être référencée simultanément par une instance de Lettre
et une instance de Colis
.
Suivi(#numSuivi, dateEmission, dateRéception)
Lettre(#id, ..., suivi=>Suivi.numSuivi) avec numSuivi unique
Colis(#id, ..., suivi=>Suivi.numSuivi) avec numSuivi unique
Contrôle : un suivi est forcément associé à une seule lettre ou un seul colis, et jamais à une lettre et un colis en même temps.
Conseil : MLD pour la suite de l'exercice
On fera le choix de transformer l'héritage par la classe mère et l'association 0..1:1 par absorption de Suivi
dans Courrier
:
BureauDePoste(#codePostal)
Adresse(#codePostal=>BureauDePoste.codePostal, #num, #rue)
CentreDeTri(#ville)
Acheminement(#centreDeTri=>CentreDeTri.ville, #bureauDePoste=>BureauDePoste.codePostal, #transport, taxeCarbone) avec Proj(Acheminement, centreDeTri) In Proj(CentreDeTri, ville) et Proj(Acheminement, bureauDePoste) In Proj(BureauDePoste, codePostal)
Courrier(#id, numSuivi, dateEmission, dateRéception, type:{lettre|colis}, timbreVert, poids, émetteurCodePostal=>Adresse.codePostal, émetteurNum=>Adresse.num, émetteurRue=>Adresse.rue, destinataireCodePostal=>Adresse.codePostal, destinataireNum=>Adresse.num, destinataireRue=>Adresse.rue) avec numSuivi unique et type, destinataireCodePostal, destinataireNum et destinataireRue non nuls
vLettre=Projection(Restriction(Courrier, type='lettre'), id, numSuivi, dateEmission, dateRéception, timbreVert, émetteurCodePostal, émetteurNum, émetteurRue, destinataireCodePostal, destinataireNum, destinataireRue)
vColis=Projection(Restriction(Courrier, type='colis'), id, numSuivi, dateEmission, dateRéception, poids, émetteurCodePostal, émetteurNum, émetteurRue, destinataireCodePostal, destinataireNum, destinataireRue)
Question
Déterminez en quelle forme normale est le schéma que vous avez posé, et faites les modifications nécessaires pour atteindre la 3NF. Justifiez.
Solution
1NF
Tous les attributs sont atomiques et toutes les relations ont au moins une clé, on est donc bien en 1NF.
2NF
Les relations
BureauDePoste
,Courrier
etCentreDeTri
sont en 2NF puisque leur clé est constituée d'un seul attribut.La relation
Adresse
est "toute-clé", donc est en 2NF.Dans la relation
Acheminement
, on a la dépendance fonctionnelletransport
→taxeCarbone
. Une partie de la clé (transport
) détermine donc un attribut non-clé (taxeCarbone
) : on n'est pas en 2NF.Voici le MLD modifié pour respecter la 2NF :
Transport(#type, taxeCarbone)
Acheminement(#centreDeTri=>CentreDeTri.ville, #bureauDePoste=>BureauDePoste.codePostal, #transport=>Transport.type)
3NF
Il n'y a aucune dépendance fonctionnelle entre des attributs non-clé dans la relation Courrier
, ce qui assure sa 3NF. En particulier, il n'y a pas de dépendance fonctionnelle entre numSuivi
et (dateEmission
, dateRéception
), puisque numSuivi
peut être nul (c'est d'ailleurs pour cela qu'il n'est pas clé). Son unicité assure d'ailleurs la non-redondance.
Pour les autres relations, la 3NF est assurée : elles sont "toute-clé" ou n'ont qu'un seul attribut en dehors de la clé.
Question
Écrivez les instructions SQL pour créer les tables des relations correspondant uniquement aux classes BureauDePoste
, CentreDeTri
et Acheminement
, en vous basant sur votre schéma relationnel normalisé.
Solution
CREATE TABLE CentreDeTri(
ville VARCHAR(30) PRIMARY KEY
);
CREATE TABLE BureauDePoste (
codePostal NUMBER(5) PRIMARY KEY
);
CREATE TABLE Transport (
type CHAR PRIMARY KEY,
taxeCarbone FLOAT
);
CREATE TABLE Acheminement(
centreDeTri VARCHAR(30),
bureauDePoste NUMBER(5),
transport CHAR,
FOREIGN KEY (centreDeTri) REFERENCES CentreDeTri(ville),
FOREIGN KEY (bureauDePoste ) REFERENCES BureauDePoste(codePostal),
FOREIGN KEY (transport) REFERENCES Transport(type),
PRIMARY KEY(centreDeTri, bureauDePoste, transport)
);