Bases de données relationnelles et normalisation :
de la première à la sixième forme normale
Date de publication : 07/09/2008. Date de mise à jour : 14/07/2012.
6. Sixième forme normale
6.1. Introduction
6.2. Définition de la sixième forme normale (6NF)
6.3. Relvars à caractère temporel, purement historiques
6.3.1. Un exemple
6.3.2. Typage des intervalles, opérateurs
6.3.3. Opérateurs PACK et UNPACK
6.3.4. Opérateurs U_PROJECT et U_JOIN
6.3.5. Opérateurs de comparaison relationnelle généralisés
6.3.6. Dépendance de jointure généralisée
6.3.7. Retour sur la 6NF, place à la concision
6.3.7.1. Remarque préalable
6.3.7.2. 2e impératif LDD
6.3.8. Déclaration des relvars
6.4. Données actives et données historisées
6.4.1. Premières tentatives de modélisation
6.4.2. Des impératifs LDD à respecter
6.4.2.1. 5e impératif LDD
6.4.2.2. 6e impératif LDD
6.4.2.3. 1er impératif LDD
6.4.2.4. 3e impératif LDD
6.4.2.5. 4e impératif LDD
6.4.2.6. Poursuite du processus d'historisation
6.5. Points particuliers
6.5.1. A propos de l'intégrité référentielle et des contraintes d'intégrité en général
6.5.2. Choix du mode d'historisation
6.5.3. Relvars « associatives »
6.5.4. Trois relvars ou une relvar unique ?
6.6. Pour conclure avec la normalisation en 6NF
6. Sixième forme normale
6.1. Introduction
A partir de 1979 et pendant près d'un quart de siècle, nous avons vécu avec la 5NF qui marquait la limite du processus
de normalisation par projection/jointure. Et puis la 6NF est venue repousser cette limite. Nous verrons les
bienfaits que l'on peut en retirer.
En attendant, reprenons la structure de la relvar F des fournisseurs (paragraphe 2.6) :
Figure 6.1 - Relvar F : valeur actuelle de la relvar
Par référence au théorème de Date et Fagin (cf. paragraphe 5.8), cette relvar respecte la 5NF, puisqu'elle
a pour unique clé candidate le singleton {Four_No} et parce qu'elle respecte par ailleurs la BCNF. Mais
rien n'interdit de pousser au maximum la décomposition de la relvar, sans perte, selon les trois projections
suivantes :
Figure 6.2 - F = JOIN {F_Nom, F_Statut, F_Ville}
En première approche, une telle décomposition n'offre aucun intérêt et relève même du « Modèle binaire »,
lequel a été rejeté par Ted Codd [Codd 1990]. Pourtant, si l'on fait référence à l'énoncé de la 6NF,
on verra que les trois relvars F_Nom, F_Statut et F_Ville sont conformes à celle-ci, tandis que la relvar F
ne l'est pas. Mais Chris Date nous prévient qu'il ne faut pas tenir compte du respect à tout crin de la 6NF,
laquelle concerne fondamentalement les relvars dotées d'attributs de type intervalle (dont les intervalles de dates),
donc les relvars à caractère historique, qui feront l'objet de toute notre attention.
Quant à elle, la relvar F a un caractère intemporel et la décomposer est en l'occurrence inutile.
Notons en passant que la relvar F (qui respecte la 5NF, rappelons-le) vérifie un certain nombre
de dépendances de jointure (DJ), dont celle qui
correspond à sa décomposition en F_Nom, F_Statut, F_Ville :
DJ1 = ★{{Four_No, Four_Nom}, {Four_No, Statut}, {Four_No, Ville}}
Dépendance de jointure qui n'est pas triviale, contrairement à celles-ci (cf. paragraphe 5.4) :
DJ2 = ★{{Four_No, Four_Nom, Statut, Ville}},
DJ3 = ★{{Four_No, Four_Nom, Statut, Ville}, {Four_No, Ville}},
Et autres de la même farine.
Commençons par définir la 6NF pour découvrir son rôle et son intérêt dans le contexte des données temporelles,
étudiées de façon complète et très approfondie dans [Date 2003].
6.2. Définition de la sixième forme normale (6NF)
Une variante intéressante :
Une relvar est en 6NF si et seulement si elle est en 5NF, elle est de degré n, et n'a aucune clé de degré
inférieur à n – 1.
La relvar F n'est pas en 6NF puisque, comme on l'a vu, elle vérifie la DJ non triviale :
DJ1 = ★{{Four_No, Four_Nom}, {Four_No, Statut}, {Four_No, Ville}}
Tandis que les relvars F_Nom, F_Statut et F_Ville sont en 6NF, car elles ne vérifient que
des DJ triviales, dont voici quelques exemples :
Relvar F_Nom : ★{{Four_No, Four_Nom}}
Relvar F_Nom : ★{{Four_No, Four_Nom}, {Four_No}}
Relvar F_Statut : ★{{Four_No, Statut}}
Relvar F_Ville : ★{{Four_No, Ville}}
Mais, répétons-le, parce qu'elle respecte la 5NF et ne comporte pas de données de type intervallaire,
du point de vue de la normalisation il est inutile de décomposer la relvar F.
En revanche, le type intervallaire est fréquent dans les bases
données du monde de la finance, de l'assurance, de la retraite, de la distribution, de la sécurité sociale, etc.,
là où la datation est omniprésente, quand l'historisation des données — entre autres —
donne bien des soucis, certes à cause des problèmes de volumétrie, mais aussi du fait de la programmation
passablement compliquée qui peut en découler :
c'est dans ce contexte que la normalisation en 6NF devient utile, comme on pourra s'en rendre compte
dans les paragraphes qui suivent. Nous déborderons sensiblement du champ de la normalisation pour pénétrer
dans celui de la modélisation des données temporelles, mais nous pensons que la visite vaut largement le détour.
6.3. Relvars à caractère temporel, purement historiques
6.3.1. Un exemple
Prenons le cas des fournisseurs avec lesquels nous avons définitivement cessé de travailler
(et qui ne figurent donc pas dans la relvar F ci-dessus). Supposons qu'au moyen d'une relvar ad-hoc
nous ayons conservé la trace des changements successifs dont ils ont fait l'objet :
Figure 6.3 - Relvar F_Cessation : fournisseurs avec lesquels nous ne travaillons plus
Ce que raconte cette relvar : par exemple, que le fournisseur Albert & a commencé à travailler avec nous
le 1er février 2005, il était alors localisé à Lille et avait un statut valant 10. Le 1er mars de la même année,
son statut est passé à 12. Le 28 mars suivant, il a déménagé à Paris. Du 2 mai au 11 juin il a cessé de travailler
avec nous. Le 12 juin, il a repris son activité et son statut est passé à 14. Le 15 juillet, il a déménagé
à Lyon. Le 1er octobre, le nom de sa raison sociale a changé, pour devenir Albert et Cie. Le 3 décembre on
le retrouve à Nantes et le 17 avril de l'année suivante à Caen (il a la bougeotte). Le 2 mai suivant, on le retrouve
à Lille et son statut est passé à 25. Le 15 juillet, sa raison sociale a encore changé de nom, pour devenir Albert.
A compter du 2 décembre 2007, nous avons cessé de travailler avec ce fournisseur.
La relvar F_Cessation peut prendre bien d'autres formes, mais commençons avec la structure que nous avons
décrite ci-dessus. Tout d'abord, cette relvar est en 5NF. En effet :
Elle a pour clés candidates les paires {Four_No, Depuis} et {Four_No, Jusqu_au}
Chaque projection participant à chaque dépendance de jointure contient les attributs composant ces clés :
|
DJ1 : ★{{Four_No, Depuis, Four_Nom}, {Four_No, Depuis, Statut}, {Four_No, Depuis, Ville}, {Four_No, Jusqu_au}}
DJ2 : ★{{Four_No, Jusqu_au, Four_Nom}, {Four_No, Jusqu_au, Statut}, {Four_No, Jusqu_au, Ville}, {Four_No, Depuis}}
DJ3 : ★{{Four_No, Depuis, Four_Nom, Statut}, {Four_No, Depuis, Ville}, {Four_No, Jusqu_au}}
DJ4 : ★{{Four_No, Depuis, Four_Nom}, {Four_No, Depuis, Statut Ville}, {Four_No, Jusqu_au}}
DJ5 : ★{{Four_No, Four_Nom, Statut, Ville, Depuis, Jusqu_au}}
Etc.
|
|
(On peut encore facilement montrer que la relvar respecte la BCNF, puis mettre à profit le 2e théorème mentionné
au paragraphe 5.8 pour montrer que la 5NF est respectée elle aussi).
|
Mais la relvar n'est pas en 6NF, parce que parmi ces dépendances de jointure, seule DJ5 est triviale.
Par exemple, DJ1 ne l'est pas, puisque la projection {Four_No, Depuis, Four_Nom} est un des éléments de
cette DJ, sans que l'attribut Ville en fasse partie. Et constat pas inintéressant, on se rend compte
que la relvar est fortement redondante : on apprend ainsi plus que de raison que, à une époque, le fournisseur
C1 s'est appelé Albert & Fils, et nous devons chercher à éliminer ce genre de redondance qui pollue
bien des relvars à caractère historique.
Comme dans le cas de la relvar F, pleins de bonne volonté, nous pourrions décomposer F_Cessation
en quatre relvars, appelons-les F_N, F_S, F_V et F_D, respectant chacune la 6NF :
Figure 6.4 - Relvar F_Cessation : Décomposition de la relvar
Figure 6.5 - Relvar F_Cessation : Décomposition de la relvar (suite)
Mais la redondance est toujours là, et qui plus est, on a maintenant quatre relvars sur les bras au lieu d'une :
piètre résultat, même si ces nouvelles relvars sont en 6NF. Néanmoins, avant de conclure un peu hâtivement que la 6NF
ne sert finalement à rien et qu'il était parfaitement inutile de l'inventer, poussons plus avant,
en étudiant de plus près le caractère intervallaire de la relvar F_Cessation. Intéressons-nous à l'esprit
de la 6NF plutôt qu'en rester à la lettre.
6.3.2. Typage des intervalles, opérateurs
La relvar F_Cessation n'est donc pas en 6NF. Pour normaliser efficacement, c'est-à-dire éliminer
les redondances, il est préférable, pour commencer, d'en passer par une représentation intervallaire des dates
de début et de fin, et bénéficier ainsi des opérateurs dédiés. Remplaçons donc la paire
d'attributs {Depuis, Jusqu_au} par un attribut unique, que nous appellerons Durant :
Figure 6.6 - Relvar F_Cessation : Représentation intervallaire
La déclaration de la relvar F_Cessation pourrait être la suivante (en Tutorial D) :
Où l'attribut Durant est du type INTERVAL_DATE dont l'étude n'est pas a priori l'objet de
cet article, mais vu son importance, il faut en dire un mot. Ce type (et plus généralement tout type
INTERVAL_TRUC) doit permettre de comparer,
manipuler des intervalles. Ainsi, doit-on déjà disposer d'un opérateur de sélection :
INTERVAL_DATE ( [deb:fin] )
deb et fin étant des expressions de type DATE.
Dans notre exemple, durant l'intervalle i = [2005-02-01:2005-02-28], le fournisseur C1 s'est appelé
Albert & Fils, il avait le statut 10 et était localisé à Lille.
Le type INTERVAL_TRUC (en particulier le type INTERVAL_DATE) présente, les caractéristiques suivantes :
| INTERVAL est un générateur de type,
au même titre que le générateur de type RELATION qu'on a utilisé pour
déclarer la relvar F_Cessation (cf. paragraphe A.2 en annexe). Le type INTERVAL_TRUC est obtenu par
invocation du générateur de type INTERVAL (cf. [Date 2004], chapitre 23 « Temporal Databases »,
ou encore [Date 2004], chapitre 5 « Point Types and Interval Types »).
Un intervalle (ou valeur d'intervalle) i du type INTERVAL_TRUC est une valeur scalaire
(« atomique » pour faire court) pour laquelle ont été définis deux opérateurs monadiques, BEGIN et END,
et un opérateur dyadique, , tels que :
1. BEGIN (i) et END (i) renvoient une valeur de type TRUC
(donc une date dans le cas du type DATE).
2. BEGIN (i) ≤ END (i).
3. Si p est une valeur du type TRUC,
alors p i est vrai
si et seulement si BEGIN (i) ≤ p et p ≤ END (i) sont vrais en même temps.
Par exemple (en Tutorial D), pour savoir quel était le statut du fournisseur C1 au 15 septembre 2005 :
(F_Cessation WHERE Four_No = 'C1' AND DATE ('2005-09-15')
Durant) {Statut}
|
| TRUC (et en particulier DATE) est le type de point
utilisé en relation avec le type d'intervalle INTERVAL_TRUC : un intervalle est une succession de points
(valeurs discrètes) et dans le cas qui nous intéresse,
ces points se succèdent dans le temps, c'est-à-dire que ce sont des dates (disons du calendrier).
Dans d'autres situations, les points peuvent être des températures, des tranches d'imposition,
des paliers de remise, etc.
|
Dans le cas général, le type de point TRUC (et donc le type de point DATE en particulier)
possède certaines propriétés :
| Il est totalement ordonné :
si v1 et v2 sont deux valeurs du même type de point TRUC et si l'expression
« v2 > v1 » est vraie, alors l'expression
« v1 > v2 » est fausse. Plus généralement, on doit disposer des opérateurs
de comparaison habituels, « = », « > », etc. Cela vaut évidemment dans le cas
particulier du type de point DATE : le système doit nous permettre de répondre par exemple à
la question « Quels fournisseurs ont commencé à travailler avec nous avant le fournisseur C3 ? »
|
| Il est doté d'opérateurs permettant de retrouver
les plus petites et les plus grandes valeurs pour ce type de point : FIRST_TRUC, LAST_TRUC,
le prédécesseur (PRIOR_TRUC) et le successeur (NEXT_TRUC) d'un point. Dans le cas du type de point
DATE : FIRST_DATE, LAST_DATE (en particulier LAST_DATE () qui correspond à la
« fin des temps »), PRIOR_DATE, NEXT_DATE.
|
Mentionnons encore la panoplie des opérateurs de comparaison connus collectivement comme étant les opérateurs
de Allen (Allen's operators), permettant de savoir si deux intervalles sont contigus (opérateur MEETS),
se recouvrent (opérateur OVERLAPS), etc.
6.3.3. Opérateurs PACK et UNPACK
|
Complétons notre boîte à outils avec les opérateurs PACK et UNPACK.
Ces opérateurs sont à la 6NF ce que le poussé
et le tiré sont au soufflet d'un accordéon, et cette paire d'opérateurs constitue une des clés
(clefs en l'occurrence...) d'une authentique (et efficace) normalisation en 6NF.
A noter que PACK et UNPACK ne sont pas des opérateurs primitifs, ce ne sont que des combinaisons d'opérateurs
relationnels existants (à ce sujet, se reporter à [Date 2003],
chapitre 8 « The PACK and UNPACK Operators »).
|
PACK
L'opérateur PACK doit nous permettre de regrouper des intervalles d'une relvar R. Sa syntaxe est la suivante :
PACK R ON A
Où R est une expression relationnelle et A un attribut de type intervallaire, appartenant à l'en-tête de R.
Soit deux intervalles i1 et i2. Leur regroupement est possible dans les conditions suivantes :
| Soit on vérifie : BEGIN (i1) ≤ END (i2)
et BEGIN (i2) ≤ END (i1), ce qui se produit quand i1 et i2 se recouvrent totalement ou
partiellement (l'opérateur de Allen OVERLAPS renvoie alors la valeur vrai).
Prenons un exemple, tiré de [Date 2004], dans lequel la relvar R comporte deux attributs S#
(codes des fournisseurs) et DURING (Périodes d'activité). L'attribut DURING est du type
INTERVAL_DATE et les dates sont symbolisées par d01, d02, etc.
Réalisons l'opération PACK suivante :
PACK R ON DURING
|
Figure 6.7 - Regroupement des intervalles par PACK, dans l'esprit de l'opérateur OVERLAPS
| Soit on vérifie : END (i1+1) = BEGIN (i2)
ou END (i2+1) = BEGIN (i1), c'est-à-dire que i1 et i2 sont contigus
(l'opérateur de Allen MEETS renvoie alors la valeur vrai). Ainsi, pour la paire {Four_No, Four_Nom}
de la relvar F_Cessation, dans le cas du couple <'C1', 'Albert & Fils'>,
on vérifie les égalités :
END ([2005-02-01:2005-02-28] + 1) = BEGIN ([2005-03-01:2005-03-27]) ;
END ([2005-03-01:2005-03-27] + 1) = BEGIN ([2005-03-28:2005-05-01]) ;
END ([2005-06-12:2005-07-14] + 1) = BEGIN ([2005-07-15:2005-09-30]).
|
En conséquence de quoi, on a tout à gagner à regrouper les intervalles pendant lesquels
le nom de la raison sociale de C1 fut 'Albert & Fils' (Figure 6.6), pour condenser
au maximum l'information :
[2005-02-01:2005-05-01] et [2005-06-12:2005-09-30]
De la même façon, le nom de la raison sociale de C1 fut 'Albert & Cie' pendant l'intervalle :
[2005-10-01:2006-07-14]
Puis 'Albert' pendant l'intervalle :
[2006-07-15:2007-12-01]
Si donc on écrit (ce que l'on appelle parfois une projection temporelle) :
PACK F_Cessation {Four_No, Four_Nom, Durant} ON Durant
Concernant le fournisseur C1, l'image du résultat est alors la suivante (appelons FND la relvar correspondante) :
Figure 6.8 - PACK F_Cessation {Four_No, Four_Nom, Durant} ON Durant
On observera que la redondance a singulièrement diminué concernant l'attribut Four_Nom,
on peut même dire qu'elle a disparu.
Si l'on fait subir la même cure d'amaigrissement à l'attribut Statut :
PACK F_Cessation {Four_No, Statut, Durant} ON Durant
Toujours pour le fournisseur C1, l'image du résultat est la suivante (appelons FSD la relvar correspondante) :
Figure 6.9 - PACK F_Cessation {Four_No, Statut, Durant} ON Durant
Dans le cas de l'attribut Ville :
PACK F_Cessation {Four_No, Ville, Durant} ON Durant
Toujours pour le fournisseur C1, l'image du résultat est la suivante (appelons FVD la relvar correspondante) :
Figure 6.10 - PACK F_Cessation {Four_No, Ville, Durant} ON Durant
On peut subodorer l'influence qu'aura l'opérateur PACK dans le travail de normalisation, mais à lui
seul il ne suffit pas, il devra le plus souvent binômer avec son inverse, UNPACK (au moins dans
le cadre théorique).
Remarque. Si la question suivante était posée : « Quand le fournisseur C1 a-t-il travaillé pour
nous ? », on pourrait a priori interroger n'importe laquelle des trois relvars FND, FSD, FVD.
Mais, pour éviter tout choix arbitraire et surtout pour en plus tenir compte des données non
historisées (cf. paragraphe 6.4), il sera préférable de mettre en oeuvre une relvar supplémentaire,
appelons-la FDD, résultat de l'opération :
PACK F_Cessation {Four_No, Durant} ON Durant
Concernant le fournisseur C1, l'image du résultat est la suivante :
Figure 6.11 - PACK F_Cessation {Four_No, Durant} ON Durant
UNPACK
L'opérateur UNPACK a pour fonction de nous permettre de dégrouper un intervalle en intervalles élémentaires,
non décomposables. Un intervalle i est élémentaire si et seulement si BEGIN (i) = END (i).
Pour parvenir à dégrouper les intervalles, on utilise cet opérateur dont la syntaxe est la suivante :
UNPACK R ON A
Où R est une expression relationnelle et A un attribut de type intervallaire, appartenant à l'en-tête de R.
Pour reprendre la relvar F_Cessation (Figure 6.6), écrivons :
UNPACK F_Cessation {Four_No, Four_Nom, Durant} ON Durant
Le résultat produit est le suivant :
Figure 6.12 - UNPACK F_Cessation {Four_No, Four_Nom, Durant} ON {Durant}
A cette occasion, on apprend 201 fois que le fournisseur C1 s'est d'abord appelé Albert & Fils,
puis 287 fois qu'il s'est appelé Albert & Cie et enfin 505 fois Albert. Ce qui fait qu'à partir
d'une (valeur de) relation F_Cessation requérant une dizaine de tuples pour le fournisseur C1
(cf. Figure 6.6), par UNPACK on produit près d'un millier de tuples, tandis que par
PACK le résultat n'est que de quatre tuples (Figure 6.8).
Mais alors, quel intérêt présente l'opérateur UNPACK si son utilisation conduit à une telle inflation,
à une consommation a priori énorme de temps et d'espace ? Disons déjà que d'un point de vue
théorique, contexte dans lequel les considérations bassement matérielles n'interviennent pas,
le principe général est de pouvoir utiliser les opérateurs relationnels
(RESTRICT, PROJECT, JOIN, UNION, etc.) selon le mode habituel, intemporel,
ce qui nécessite de procéder en trois temps :
|
1) Transformer par UNPACK un intervalle quelconque en intervalles élémentaires,
2) Effectuer l'opération relationnelle voulue, dans laquelle sont
impliqués ces intervalles élémentaires,
3) Regrouper par PACK les intervalles élémentaires contigus.
|
Au passage, je cite et traduis [Date 2003], paragraphe A.8 :
|
« L'opérateur UNPACK est un composant conceptuel qui joue un rôle crucial dans notre approche de la
gestion des données intervallaires en général, et des données temporelles en particulier. UNPACK a été
très critiqué dans la littérature, au motif qu'il ne peut en résulter qu'une piètre performance. [...]
Mais ça n'est qu'un composant conceptuel. Du moment qu'on peut s'en passer, nous ne tenons pas à ce que
les opérations de décompression aient physiquement lieu. »
|
Il est entendu que, dans ce genre d'opération, le but n'est pas de gâcher inutilement des ressources
en espace et en temps qui ne peuvent qu'être considérables, pour finir à tout coup par une annulation
de la tâche. En ce sens, [Date 2003] montre quelle doit être l'approche d'un optimisateur
dans l'exploitation de UNPACK (Cf. son appendice A : « Implementation Considerations »).
La matérialisation du résultat n'est évidemment à effectuer qu'à la demande expresse de l'utilisateur.
6.3.4. Opérateurs U_PROJECT et U_JOIN
Revenons à la 6NF : elle se situe dans le prolongement de celles qui l'ont précédée,
de la deuxième à la cinquième, c'est-à-dire qu'à l'aide du marteau PROJECT on « casse » une
relation r non normalisée en deux (ou plusieurs) relations, que l'on sait « recoller »
par JOIN pour retrouver très exactement la relation r (principe de la décomposition sans perte
de données, nonloss decomposition).
Pour le moment, nous savons que la relvar F_Cessation est en 5NF, c'est-à-dire égale à
la jointure de ses projections :
|
F_Cessation = F_Cessation {Four_No, Four_Nom, Durant}
JOIN
F_Cessation {Four_No, Statut, Durant}
JOIN
F_Cessation {Four_No, Ville, Durant} ;
|
Nous savons aussi que ces projections sont en 6NF, car non décomposables par projection et ne comportent
donc que des DJ triviales :
|
DJ1 : ★{{Four_No, Four_Nom, Durant}}
DJ2 : ★{{Four_No, Statut, Durant}}
DJ3 : ★{{Four_No, Ville, Durant}}
|
Mais nous savons également que la relvar F_Cessation n'est pas en 6NF, car elle comporte la DJ non triviale :
★{{Four_No, Four_Nom, Durant}, {Four_No, Statut, Durant}, {Four_No, Ville, Durant}}
Ce dont nous avons besoin, c'est d'un mécanisme qui permette, par projection, de décomposer F_Cessation
en relvars « compressées », à savoir FND, FSD et FVD, FDD (cf. Figure 6.8 et suivantes) et,
toujours selon le principe de la décomposition sans perte, d'avoir l'assurance que par leur jointure
on retrouve très exactement F_Cessation.
Cela passe en fait par la généralisation des opérateurs de l'algèbre relationnelle (rien que ça !),
ceux que nous connaissions jusqu'ici (RESTRICT, PROJECT, JOIN, UNION, etc.) n'étant plus que des cas
particuliers des opérateurs généraux (RENAME étant le seul opérateur non concerné par la généralisation).
Étant donné un opérateur relationnel traditionnel symbolisé par OPERATEUR, par généralisation celui-ci
donne lieu à l'opérateur U_OPERATEUR, lequel au plan théorique fonctionne comme on l'a vu ci-dessus :
-
Décomposition par UNPACK d'un intervalle quelconque en intervalles élémentaires,
-
Exécution de l'opération relationnelle traditionnelle voulue,
à l'aide de l'opérateur OPERATEUR, et dans laquelle sont impliqués ces intervalles élémentaires,
-
Regroupement par PACK des intervalles élémentaires contigus.
Si l'on passe du plan théorique au plan pratique, on observera que pour des raisons évidentes de performance,
un moteur relationnel se doit de court-circuiter l'opérateur UNPACK tant que faire se peut
(cf. par exemple le cas de l'opérateur U_PROJECT).
Quoi qu'il en soit, dans le cadre de la normalisation par projection/jointure, nous nous limiterons aux
deux opérateurs U_PROJECT et U_JOIN (U pour USING, ou UNPACKING, au choix).
U_PROJECT
La « U_projection » est notée :
USING (ACL)
R {BCL}
Expression dans laquelle R désigne une expression relationnelle et BCL la liste de noms d'attributs
de l'en-tête de R qui sont utilisés pour effectuer la projection traditionnelle R {BCL}
(cf. Annexe B). ACL représente une liste de noms d'attributs (
Attribute Comma List) de l'en-tête
de R, qui sont de type intervallaire et font partie de la liste BCL.
Cette expression est équivalente à :
PACK ((
UNPACK R
ON (ACL)) {BCL})
ON (ACL)
Expression qui peut être réduite à celle-ci, dans laquelle on court-circuite l'opérateur UNPACK
(et dans ce cas on évite la manipulation des intervalles élémentaires pour eux-mêmes) :
PACK (R {BCL})
ON (ACL)
Ainsi, l'exemple que l'on a vu ci-dessus :
PACK F_Cessation {Four_No, Four_Nom, Durant}
ON Durant
est la U_Projection dont le résultat FND est représenté dans la Figure 6.8 :
USING Durant
F_Cessation {Four_No, Four_Nom, Durant}
Notes :
-
Si la liste ACL ne comporte qu'un seul nom d'attribut, on peut supprimer les parenthèses,
comme on vient de le faire avec l'attribut Durant.
-
Si la liste ACL est vide, on retrouve la projection traditionnelle.
U_JOIN
La « U_jointure » est notée :
USING (ACL)
R1
JOIN R2
Expression relationnelle dans laquelle R1
JOIN R2 représente la jointure
(naturelle) traditionnelle
(cf. Annexe B) et ACL représente une liste de noms d'attributs de l'en-tête de R1 et de celui de R2,
attributs qui sont de type intervallaire.
Cette expression est un raccourci pour celle-ci :
|
PACK ((UNPACK R1 ON (ACL))
JOIN
(UNPACK R2 ON (ACL)))
ON (ACL)
|
On retrouve une fois de plus le grand principe du fonctionnement du tandem PACK/UNPACK
(sachant qu'en réalité l'étape UNPACK peut, là encore, être court-circuitée) :
-
Décomposition par UNPACK d'un intervalle quelconque en intervalles élémentaires,
-
Exécution de l'opération relationnelle traditionnelle voulue, dans laquelle sont
impliqués ces intervalles élémentaires,
-
Regroupement par PACK des intervalles élémentaires contigus.
Ainsi, la U_jointure des U_projections FND et FSD (cf. Figure 6.8 et Figure 6.9) donnera lieu au
résultat suivant concernant le fournisseur C1 (appelons-le FNSD) :
Figure 6.13 - U_jointure des U_projections FND et FSD
A son tour, la U_jointure de FNSD et de FVD redonnera la relvar initiale F_Cessation.
Grâce aux opérateurs U_PROJECT et U_JOIN, on a ainsi pu réaliser une U_projection / U_jointure
sans perte, faisant que l'on peut remplacer la relvar F_Cessation par le trio FND, FSD, FVD.
Avant d'en reparler en relation avec la 6NF, reste à évoquer les opérateurs de comparaison relationnelle
généralisés et la dépendance de jointure généralisée.
Note : La U_jointure est associative, et encore une fois l'optimiseur peut jouer là-dessus, ainsi que sur
la concision de l'expression générale
USING (ACL)
R1
JOIN R2
,
qui autorise une grande liberté dans la façon de construire les algorithmes pour implémenter l'opérateur.
6.3.5. Opérateurs de comparaison relationnelle généralisés
En algèbre relationnelle, deux relations R1 et R2 peuvent être comparées à l'aide des opérateurs
ensemblistes de comparaison relationnelle : « = » (R1 est égale à R2), « ≠ »
(R1 n'est pas égale à R2), «
»
(R1 est incluse dans R2), «
»
(R1 est strictement incluse dans R2),
«
» (R1 inclut R2),
«
»
(R1 inclut strictement R2). Le résultat de la comparaison est une valeur de vérité :
vrai,
faux.
Par exemple, si l'on se reporte à la Figure 2.6, pour savoir si la projection des fournisseurs sur
l'attribut Ville est égale à la projection des pièces sur ce même attribut, l'expression relationnelle
suivante doit être vraie :
F {Ville} = P {Ville}
/* Si
vrai, alors réponse affirmative */
Ou encore, pour savoir si certains fournisseurs n'ont pas livré de pièces :
F {Four_No}
FP {Four_No}
/* Si
vrai, alors réponse affirmative */
Ce qui vaut pour les opérateurs JOIN, PROJECT, etc. à savoir, comme on l'a vu, qu'ils sont
généralisables en U_JOIN, U_PROJECT, etc., vaut également pour les opérateurs de comparaison
relationnelle. Ainsi, la contrepartie de l'opérateur de comparaison « = » est l'opérateur
« U_= ». Plus précisément, l'expression :
USING (ACL)
R1
= R2
est une abréviation pour :
(
UNPACK R1
ON (ACL))
= (
UNPACK R2
ON (ACL))
Où chaque attribut mentionné dans ACL est de type intervalle et figure à la fois dans R1 et R2.
A noter que la participation de l'opérateur PACK est inutile, puisque le résultat n'est pas une
relation mais seulement une valeur de vérité,
vrai/faux.
On dit que les relations R1 et R2 sont équivalentes par rapport à ACL. Pour reprendre un exemple de
[Date 2003], étant données les relations :
« R1 = R2 » est
faux, mais «
USING A
R1
= R2
» est
vrai.
6.3.6. Dépendance de jointure généralisée
A son tour, la dépendance de jointure fait l'objet d'une généralisation.
Soit R une relvar, A, B, ..., Z des sous-ensembles d'attributs de l'en-tête de R, ACL une liste
d'attributs de type intervalle de l'en-tête de R, et soit R
′ la U_jointure des U_projections de R
sur A, B, ..., Z.
R vérifie la dépendance de jointure (généralisée)
USING (ACL) ★{A, B, ..., Z}
si et seulement si
USING (ACL)
R
= R
′
est
vrai. Considérons à nouveau la relvar F_Cessation de la Figure 6.6 :
Figure 6.14 - Relvar F_Cessation : Représentation intervallaire (bis)
Cette relation a pour U_projections les relvars FND, FSD et FVD (cf. Figure 6.8 et suivantes).
On a vu au paragraphe 6.3.4 que la U_jointure de ces U_projections permet de retrouver
F_Cessation (U_projection et U_jointure sans perte de données).
Ainsi, R représentant F_Cessation et R
′ la U_jointure de ses U_projections, l'expression
USING (ACL)
R
= R
′
est vraie. Autrement dit, F_Cessation vérifie la dépendance de jointure généralisée
USING Durant ★{FND, FSD, FVD}
Et cette dépendance de jointure est non triviale. C'est ici l'occasion de reparler de la 6NF.
6.3.7. Retour sur la 6NF, place à la concision
6.3.7.1. Remarque préalable
Reprenons la définition de la 6NF :
|
Une relvar est en sixième forme normale (6NF) si et seulement si, quelle que soit la dépendance
de jointure à laquelle elle satisfait, cette dépendance est triviale.
|
La relvar F_Cessation (cf. Figure 6.14) n'est pas en 6NF, puisqu'on a vu qu'elle
vérifie la DJ non triviale :
|
USING Durant ★{FND, FSD, FVD}
|
Il y a deux méthodes pour normaliser en 6NF une telle relvar comportant des attributs intervallaires,
la bonne et la mauvaise.
|
La bonne méthode consiste donc à décomposer F_Cessation à coups de U_projections et
ainsi éliminer les redondances non triviales, pour produire des relvars à l'image de FND, FSD et FVD
(cf. Figure 6.8 et suivantes) ;
|
|
La mauvaise méthode consiste à décomposer F_Cessation à coups de projections traditionnelles, sans tenir
compte de la dimension temporelle des données, faisant que l'on conserve les redondances
(cf. Figure 6.3 et suivantes). En outre, la relvar F_D (cf. Figure 6.5) et reprise ci-dessous
(cf. Figure 6.15), viole la 2e des 9 règles impératives (Nine Requirements) — que j'appellerai
Impératifs LDD par référence à leurs auteurs —, énoncés dans [Date 2003],
dont la programmation sous forme de contraintes ne pose pas de problème particulier, et auxquels doit se conformer
une base de données temporelle. Ce 2e impératif a pour objet de nous contraindre à grouper les intervalles
consécutifs, éliminer l'atomisation en intervalles inutilement trop fins.
|
6.3.7.2. 2e impératif LDD
L'énoncé du 2e impératif LDD est le suivant :
|
Si la base de données montre que tel fournisseur est sous contrat à j et j+1, alors il y a exactement
un tuple exprimant ce fait.
|
Or, dans la relation F_D (recopiée ci-dessous), un premier tuple montre que le fournisseur C1 a travaillé pour
nous le 28/02/2005 et un 2e tuple montre que ce même fournisseur a aussi travaillé pour nous le
lendemain : alors qu'avec F_D on a pu respecter la 6NF à la lettre, mais certainement pas dans
l'esprit, on enfreint les impératifs LDD, qui sont là pour nous mettre en garde.
L'objet de l'article étant limité — en principe — à l'étude de la normalisation, nous
n'approfondissons pas ici celle des 9 impératifs, mais on pourra se reporter avec grand profit,
soit à [Date 2003] où ils sont étudiés en détail, soit à [Date 2004] qui en
fournit une synthèse, ou encore au
support de cours
fourni par Hugh Darwen.
En tout cas, pour être en conformité avec le 2e impératif LDD, F_D doit subir une cure d'amaigrissement,
être réduite à F_D
′ comme le montre l'image
ci-dessous, où l'on voit qu'une surabondance de mauvais aloi fait place à la concision :
Figure 6.15 - Relvars F_D et F_D′, place à la concision
Si l'on remplaçait F_D par la table historisant les données relatives aux dix millions de clients
d'une banque, ou mieux, celles celles d'un organisme à la dimension de la Sécurité sociale, on peut
imaginer l'intérêt qu'il y aurait à respecter la 6NF et les impératifs. Plus généralement, cela vaut
pour certaines bases de données réputées
« énormes », comportant des tables de dizaines de milliards de lignes, truffées de dates.
|
Le contenu de la relvar Affecter de la Figure 4.2 donne matière à réfléchir quant au respect du 2e impératif LDD...
|
Nous nous intéresserons un peu plus loin aux autres impératifs LDD.
6.3.8. Déclaration des relvars
Si on normalise la relvar F_Cessation en 6NF, c'est-à-dire si on la remplace par ses U_projections
FND, FSD, FVD et par FDD (Cf. Figure 6.8 et suivantes), celles-ci feront l'objet d'une déclaration
dans laquelle on pourra faire figurer deux nouvelles contraintes, à savoir PACKED ON et WHEN/THEN.
Avec la contrainte PACKED ON, on demande au système que les valeurs prises par une relvar (valeurs
qui sont des relations) soient toujours sous forme compressée. Par exemple :
|
VAR FSD
BASE RELATION { Four_No CHAR,
Statut INTEGER,
Durant INTERVAL_DATE }
PACKED ON Durant
KEY {Four_No, Durant} ;
|
Ceci permet de se prémunir contre les redondances et d'assurer la concision (cf. paragraphe 6.3.7).
Avec la contrainte WHEN/THEN, on demande au système de garantir l'unicité des clés candidates
dans la version décompressée d'une relvar :
|
VAR FSD
BASE RELATION { Four_No CHAR,
Statut INTEGER,
Durant INTERVAL_DATE }
PACKED ON Durant
WHEN UNPACKED ON Durant
THEN KEY {Four_No, Durant}
KEY {Four_No, Durant} ;
|
Ceci permet de garantir la règle : « A un jour donné, un fournisseur n'a qu'un
statut », donc de se prémunir contre des contradictions telles que celle-ci-dessous
(conduisant à défaut à développer des contraintes faisant intervenir les opérateurs MEETS et OVERLAPS)
et auxquelles sont exposées les relvars FND, FSD et FVD (mais pas FDD) :
Figure 6.16 - Contradiction, car un fournisseur ne peut pas avoir deux statuts le même jour
N.B. Toujours dans le but d'assurer la concision, la déclaration précédente peut être remplacée par la suivante :
|
VAR FSD
BASE RELATION { Four_No CHAR,
Statut INTEGER,
Durant INTERVAL_DATE }
USING KEY {Four_No, Durant} ;
|
La paire {Four_No, Durant} constitue alors ce que l'on appelle une « U_Key ».
6.4. Données actives et données historisées
6.4.1. Premières tentatives de modélisation
Ce qui suit concerne évidemment la 6NF, mais il s'agit de considérations ayant fondamentalement trait aux données
temporelles, et il n'est pas inutile d'en parler dans le contexte de la modélisation.
Nous avons traité de la 6NF à partir de l'exemple d'une relvar F_Cessation (cf. Figure 6.14) comportant un attribut de
type intervallaire (attribut Durant). Prenons aussi en compte les informations actives,
« en cours ». Remontons le temps et situons-nous au 1er janvier 2005 : à cette date est née une
relvar de base semblable à la relvar F (cf. Figure 6.1) et répondant au doux nom de F_Depuis.
Le 3 février de la même année elle héberge ses deux premiers tuples :
Figure 6.17 - Relvar F_Depuis, données actives
Comme on historise les données, associons à F_Depuis une relvar, appelons-la F_Histo, pour
laquelle on reprend la structure de la relvar F_Cessation (cf. Figure 6.14) et dont la valeur
initiale est la suivante (cardinal 0) :
Figure 6.18 - Relvar F_Histo, valeur initiale
Le 1er mars, le fournisseur C1 change de statut et l'on en historise la valeur initiale 10, mais on serait
bien en peine a priori de déterminer la valeur prise par l'attribut Durant, car quelle est la date de début
correspondante ? S'agit-il du 1er janvier (date de naissance de F_Histo),
du 3 février, date à laquelle la relvar F_Depuis a changé de valeur, ou d'une quelconque autre date,
antérieure au 1er mars ? L'usage veut que l'on enrichisse la structure de la relvar F_Depuis en la
dotant d'un attribut correspondant à ce que les informaticiens ont coutume d'appeler
« la date de début » (fixée par l'utilisateur au 1er février pour les fournisseurs C1 et C2,
date à laquelle l'entreprise a passé contrat avec eux).
Appelons Depuis cet attribut. La valeur de la relvar restructurée F_Depuis est celle-ci :
Figure 6.19 - Relvar F_Depuis : prise en compte du temps
Ainsi, on est à même de renseigner correctement F_Histo quand le statut du fournisseur
C1 passe de la valeur 10 à la valeur 12, avec effet au 1er mars. Les relvars deviennent :
Figure 6.20 - Historisation au 1er mars
Mais déjà un problème se pose : on enfreint le 2e impératif LDD
(cf. paragraphe 6.3.7), puisqu'on apprend que le fournisseur C1 est sous contrat le 28
février 2005 (relvar F_Histo), et qu'il en est de même le lendemain, 1er mars (relvar F_Depuis).
Qui plus est, on enfreint le 5e impératif LDD défini ci-dessous.
6.4.2. Des impératifs LDD à respecter
6.4.2.1. 5e impératif LDD
L'énoncé du 5e impératif LDD est le suivant :
|
Si la base de données montre que tel fournisseur a le même nom à j et j+1, alors il y a exactement
un tuple exprimant ce fait (même principe concernant son statut et son lieu de résidence).
|
On est en infraction parce que le nom de la raison sociale (Albert & Fils) du fournisseur
C1 n'a pas changé entre le 28 février (cf. relvar F_Histo) et le lendemain, 1er mars (cf. relvar F_Depuis).
De la même façon, on constate qu'à ces deux dates il réside toujours au même endroit (Lille).
Pour se mettre en règle, on est conduit à n'historiser que les seules données ayant été modifiées
(en l'occurrence le statut du fournisseur C1). A cet effet (voir ci-dessous) on décompose la relvar F_Histo par
projection selon les relvars FNH, FSH, FVH (et FDH tant qu'à faire), à l'image des relvars
FND, FSD, FVD (et FDD) représentées Figure 6.8 et suivantes. Ainsi, puisque lui seul a changé, on historise
le statut du fournisseur C1 (dans la relvar FSH), et c'est tout.
Dans ces conditions, la relvar F_Histo passe au fil du rasoir d'Ockham et disparaît.
Les valeurs des relvars obtenues par projection sont les suivantes :
FNH - Au 1er mars 2005, aucun nom de raison sociale n'a été changé :
Figure 6.21 - Historique des noms des raisons sociales (cardinal 0)
FSH - Au 1er mars 2005, le fournisseur C1 a changé de statut :
Figure 6.22 - Historique des statuts
FVH - Au 1er mars 2005, aucun fournisseur n'a changé de lieu de résidence :
Figure 6.23 - Historique des lieux de résidence (cardinal 0)
FDH - Au 1er mars 2005, il n'y a eu aucune interruption de contrat :
Figure 6.24 - Historique des contrats (cardinal 0)
(Notons en passant que les contrats peuvent connaître des interruptions, lesquelles doivent être historisées.
Il est des cas de figure dans lesquels ces interruptions sont monnaie courante, se reporter par exemple au
« suivi » par les caisses de retraite des périodes de carrière des salariés dans les entreprises).
Cette fois-ci, le 2e et le 5e impératifs LDD sont respectés (le 2e met en jeu les relvars F_Depuis et FDH,
tandis que le 5e met en jeu les relvars F_Depuis et FSH pour les statuts).
Mais nous ne sommes pas au bout de nos peines,
il y a en effet infraction par rapport au 6e impératif LDD qui suit (c'est le parcours du combattant...)
6.4.2.2. 6e impératif LDD
L'énoncé du 6e impératif LDD est le suivant :
|
Si la base de données montre que tel fournisseur a tel nom au jour j, alors elle doit aussi montrer que
ce fournisseur est sous contrat ce même jour (même principe concernant son statut et son lieu de résidence).
|
Beau défi, et comme dirait Raoul Volfoni : « C'est du brutal... » Au 1er février le fournisseur C1
avait le statut 10 (relvar FSH, cf. Figure 6.22), mais ni la relvar F_Depuis ni la relvar FDH ne nous permettent
de savoir de manière formelle que ce jour-là ce fournisseur était sous contrat, alors que c'est leur rôle
et pas celui de la relvar FSH qui a pour unique objet de nous renseigner de façon explicite sur les statuts
passés et rien d'autre.
Concernant le nom de la raison sociale du fournisseur C1 (Albert & Fils), les relvars FNH et FDH étant vides,
ne reste que la relvar F_Depuis pour nous renseigner. Mais en l'état, tout ce que l'on peut conjecturer est que le
fournisseur C1 s'appellerait Albert & Fils (et serait sous contrat) depuis peut-être le 1er mars
2005 ; malheureusement cela est faux car en réalité la bonne date est celle du 1er février.
Aux grands maux, les grands remèdes, en un mot comme en cent, il n'y a pas trente-six solutions pour
se tirer d'affaire :
|
On adjoint à chaque attribut de la relvar F_Depuis un attribut de type date qui lui est propre,
dès lors que les données correspondantes sont sujettes à historisation. De cette façon, on saura exactement
depuis quand un fournisseur est sous contrat, de quand date son nom actuel, son statut, sa localisation.
|
Bref, quitte à ne respecter que la 5NF (ce qui en l'occurrence est suffisant), la structure de la relvar
F_Depuis évolue et sa valeur au 1er mars 2005 est la suivante :
Figure 6.25 - Relvar F_Depuis : valeur à ce jour, par attribut de la relvar
On sait désormais que c'est depuis le 1er février 2005 (attribut Four_No_Deb) que le fournisseur C1 est sous contrat,
sans interruption jusqu'à ce jour car FDH est vide.
On peut vérifier que l'on n'est plus en infraction par rapport au 6e impératif LDD. En effet :
|
Selon la relvar F_Depuis, à dater du 1er février 2005 (attribut Four_Nom_Deb), le nom de la raison sociale
du fournisseur C1 est Albert & Fils,
et depuis cette date ce fournisseur est sous contrat (attribut Four_No_Deb).
Du 1er février 2005 au 28 février 2005, le statut du fournisseur C1 valait 10 (relvar FSH) et pendant cette période
il était sous contrat (c'est ce que dit la relvar F_Depuis, attribut Four_No_Deb). Depuis le 1er mars 2005 son statut
vaut 12 et à cette date, il est sous contrat (relvar F_Depuis, attribut Four_No_Deb).
Selon la relvar F_Depuis, à dater du 1er février 2005 (attribut Ville_Deb), le fournisseur C1 réside à Lille,
et à cette date, il est sous contrat (attribut Four_No_Deb).
|
Du fait de ces structures, le travail du développeur sera incontestablement simplifié et sécurisé dans la manipulation
des données temporelles. En ce sens, il n'est pas inutile d'avoir aussi à l'esprit les 1er, 3e et 4e
impératifs LDD (courage !)
6.4.2.3. 1er impératif LDD
L'énoncé du 1er impératif LDD est le suivant :
|
Si la base de données montre que tel fournisseur est sous contrat au jour j, alors il y a exactement
un tuple exprimant ce fait.
|
Cette règle permet de s'assurer qu'il n'y a pas de redondance en ce sens. Seules sont concernées les relvars F_Depuis
(attribut Four_No_Deb) et FDH. En l'occurrence, le 1er impératif LDD est bien respecté, puisque pour le
fournisseur C1 l'attribut Four_No_Deb de la relvar F_Depuis vaut '2005-02-01' et que C1 est absent de FDH.
6.4.2.4. 3e impératif LDD
L'énoncé du 3e impératif LDD est le suivant :
|
Si la base de données montre que tel fournisseur est sous contrat au jour j, alors elle doit aussi montrer que
ce fournisseur a un nom ce même jour (ainsi qu'un statut et un lieu de résidence).
|
Cet impératif (qui est le symétrique du 6e) permet de s'assurer qu'il n'y a pas d'information absente
(nom de la raison sociale, statut, lieu de résidence) quand un fournisseur est sous contrat, quel que soit j.
Par exemple, le fournisseur C1 est sous contrat le 1er février 2005 (Figure 6.25 : relvar F_Depuis,
attribut Four_No_Deb).
On apprend une seule fois que ce même jour il s'appelle Albert & Fils (relvar F_Depuis),
que son statut vaut 10 (relvar FSH) et qu'il réside à Paris (relvar F_Depuis). De même,
au 1er mars 2005 le fournisseur C1 est toujours sous contrat, et si son statut vaut désormais 12
(relvar F_Depuis), sa raison sociale est encore Albert & Fils et il réside toujours à Paris (relvar F_Depuis).
6.4.2.5. 4e impératif LDD
L'énoncé du 4e impératif LDD est le suivant :
|
Si la base de données montre que tel fournisseur a un nom au jour j, alors il y a exactement un tuple
exprimant ce fait (même principe concernant son statut et son lieu de résidence).
|
Par exemple, selon la relvar F_Depuis, le fournisseur C1 n'a bien qu'un nom au jour j (grâce à la clé {Four_No}
et du fait que la relvar FNH ne contient rien concernant ce fournisseur). De même, on peut vérifier que
C1 n'a qu'un statut et ne réside qu'à un endroit au jour j.
Cette règle a à voir avec la redondance.
6.4.2.6. Poursuite du processus d'historisation
On n'a effectué jusqu'ici qu'une seule modification : le 1er mars 2005, le fournisseur C1 a changé de statut.
Supposons maintenant qu'il a déménagé à Paris le 28 mars. L'état de la base de données devient alors le suivant :
Figure 6.26 - État de la base données au 28 mars 2005
Le fait que le fournisseur C1 a déménagé s'est traduit par l'historisation dans la relvar FVH de son passé lillois,
tandis que la relvar F_Depuis a été mise à jour pour tenir compte de sa nouvelle localisation (ach, Paris!)
On peut vérifier que les impératifs LDD sont respectés.
Continuons à suivre le parcours mouvementé du fournisseur C1. A dater du 2 mai 2005 il n'est plus sous contrat,
mais on a décidé de conserver l'historique de ses données. L'état de la base de données est alors le suivant :
Figure 6.27 - État de la base données au 2 mai 2005
Le 12 juin 2005, le fournisseur C1 est à nouveau sous contrat, avec la même raison sociale,
il habite toujours Paris, mais son statut vaut 14. L'état de la base de données devient alors le suivant :
Figure 6.28 - État de la base données au 12 juin 2005
On peut vérifier que les impératifs LDD sont respectés.
Poursuivons. Nouvel événement : le 15 juillet 2005, le fournisseur C1 déménage à Lyon (Ciao Paris...)
L'état de la base de données devient le suivant :
Figure 6.29 - État de la base données au 15 juillet 2005
Supposons qu'au fil du temps, le fournisseur C1 fasse l'objet des changements suivants :
- Le 1er octobre 2005, changement du nom de sa raison sociale, qui devient 'Albert & Cie',
- Le 3 décembre 2005, déménagement à Nantes,
- Le 17 avril 2006, déménagement à Caen,
- Le 2 mai 2006, déménagement à Lille et changement de son statut qui passe à 25,
- Le 15 juillet 2006, changement du nom de sa raison sociale, qui devient 'Albert'.
A partir de là, calme plat.
Au 15 juillet 2006, l'état de la base de données est le suivant, exhaustif, concis et non redondant :
Figure 6.30 - État de la base données au 15 juillet 2006
Voilà une affaire qui roule. L'étude de la 6NF nous a fait voyager dans le temps, mais de façon raisonnée.
6.5. Points particuliers
6.5.1. A propos de l'intégrité référentielle et des contraintes d'intégrité en général
Si on examine la Figure 6.27, on se rend compte que la relvar F_Depuis ne peut pas être référencée
par les relvars FNH, FSH, FVH et FDH et l'on peut compter sur les gendarmes de l'intégrité référentielle (IR)
pour nous rappeler sèchement à l'ordre et exiger que cette lacune soit comblée, même si l'IR n'est pas la panacée.
Toutefois, si pour les satisfaire, on conservait le fournisseur C1 dans la relation F_Depuis, à quoi
ressemblerait le tuple correspondant ? Nombre de ceux qui pratiquent SQL marqueraient à NULL
tous les attributs autres que Four_No, ou concocteraient des valeurs spéciales, folkloriques, du genre
'9999-12-31' pour les dates, ou encore marqueraient ce tuple comme « logiquement supprimé »,
mais nous ne sommes pas là pour bricoler. En fait, tout ce qu'on peut exiger de notre part est la
parfaite cohérence des données constitutives du fournisseur C1. En l'espèce, cela concerne les relvars
FNH, FSH, FVH et FDH, et cette cohérence est effective parce que nous respectons les impératifs
LDD 3 et 6 : nous sommes irréprochables.
Ainsi, quand un fournisseur fait l'objet d'une interruption de contrat, à l'instar du fournisseur C1
il peut être supprimé sans risque de la relvar F_Depuis.
On trouvera dans [Date 2003] au chapitre 12 « Integrity Constraints II:
General Constraints » des pages fort intéressantes, détaillant la programmation de l'ensemble des
contraintes permettant de garantir les impératifs LDD, donc l'intégrité des données dans le contexte
de leur historisation. Cela évitera aux développeurs de perdre inutilement du temps à réinventer l'eau chaude
et à concevoir la programmation de ces contraintes (programmation qui du reste peut être sous-traitée au SGBD,
sous le capot) : nous les renvoyons aux développements proposés dans le chapitre 12 mentionné.
6.5.2. Choix du mode d'historisation
Certains chefs de projets préconisent que, lors de l'interruption de son contrat, les données
du fournisseur C1 soient transférées (disons archivées, par exemple pour des raisons légales, à des fins de preuves)
dans une relvar ad-hoc, du genre F_Cessation (cf. paragraphe 6.3)
et donc que l'on n'en conserve aucune trace, tant dans la relvar F_Depuis que dans les relvars
FNH, FSH, FVH et FDH.
Cet archivage peut être effectué dans une base de données dédiée, dans la mesure où l'on ne s'intéresse
plus à ce genre de données dans le cadre normal de la Production, mais seulement de façon exceptionnelle.
Si l'ex-fournisseur C1 faisait à nouveau l'objet d'un contrat, il serait à considérer
comme un tout nouveau fournisseur, avec un numéro de fournisseur Four_No dont la valeur différerait
de 'C1'. Cette approche est parfaitement légitime et offre des avantages, ne serait-ce qu'au plan
de la simplicité, mais, rappelons-le, il est des situations dans lesquelles les interruptions
temporelles sont normales, sans pour autant que l'on supprime toutes les données et, comme
on l'a déjà signalé, c'est par exemple ainsi que l'on peut procéder dans les caisses de retraite,
dans le cadre du suivi des périodes de carrière des salariés des entreprises, des artisans, professions
libérales, etc., puisqu'il peut y avoir des interruptions dans les cotisations (changement de caisse,
chômage, etc.), suivies de reprises de celles-ci. En tout état de cause, nous considérons ici
qu'une interruption de contrat peut être considérée comme temporaire (d'où l'état de la base de données
au 12 juin 2005 à l'occasion du retour du fournisseur C1, cf. Figure 6.27 & Figure 6.28) :
on conserve le passé dans les relvars FNH, FSH, FVH et FDH
et l'on en débarrasse la relvar F_Depuis (d'où une perte de surpoids bénéfique pour la performance
des traitements de masse).
6.5.3. Relvars « associatives »
Si l'on se reporte à la Figure 2.6 (paragraphe 2.6), on est amené à se poser la question suivante :
On a traité jusqu'ici de l'historisation des données propres aux fournisseurs (relvar F), mais qu'en est-il
des données qui en dépendent, par exemple celles qui sont contenues dans la relvar associative FP
(pièces livrées) ? Nous ne chercherons pas dans cet article à poursuivre le travail esquissé jusqu'ici,
le lecteur intéressé approfondira lui-même cette histoire en étudiant plus avant [Date 2003],
en se frottant aux impératifs LDD 7, 8 et 9 établis justement pour garantir l'historisation
correcte des données concernant les relvars telles que FP (historisation des relations (associations) au sens Merise).
6.5.4. Trois relvars ou une relvar unique ?
Peut-on remplacer les trois relvars FNH, FSH et FVH par une relvar unique F_Histo qui en serait leur U_jointure ?
Figure 6.31 - Relvar F_Histo hypothétique
Une telle relvar serait à même de séduire les développeurs qui le plus souvent voient d'un bon oeil
l'accès à une seule relvar et sont peu favorables à la prétendue « complexité des jointures »
(la rengaine). De même, la Production informatique serait partante, parce qu'elle préfère n'avoir
qu'un minimum de fichiers de sauvegarde et autres à gérer, etc. Mais, la mauvaise nouvelle est que
la relvar F_Histo n'est malheureusement pas égale à la U_jointure de FNH, FSH et FVH (cf. Figure 6.30).
En effet, sans même produire le résultat final, considérons déjà la relvar FNSH, U_jointure de FNH et FSH :
Figure 6.32 - U_jointure de FNH et FSH
Par U_projection, on obtient les relvars FNH2 et FSH2, et l'on voit que la valeur de
FNH2 n'est pas égale à celle de FNH :
Figure 6.33 - U_projection de FNSH
De fait, au vu de FNH2, on a perdu l'information selon laquelle le nom de la raison sociale du fournisseur C1
était Albert & Cie pendant la période allant du 2006-05-02 au 2006-07-14. Ceci s'explique par le
fait que ce fournisseur a obtenu le statut 25 le 2006-05-02, statut toujours en vigueur
(cf. la relvar F_Depuis) : cette valeur du statut et donc la période en cause
([2006-05-02:2006-07-14]) ne peuvent pas figurer dans la relation FSH,
sinon on enfreindrait le 4e impératif LDD.
6.6. Pour conclure avec la normalisation en 6NF
L'étude de la 6NF conduit inévitablement à s'intéresser de très près aux données temporelles, aux redondances
qu'elles occasionnent, à leur concision (pas d'atomisation inutile, donc de tuples superflus) et à leur cohérence,
ce qui nous fait
très largement déborder du strict cadre de cette ultime forme normale qu'est la 6NF (selon la normalisation
par projection/jointure), mais qui autrement ne présenterait guère d'intérêt qu'au plan académique. Il reste
énormément à dire, ne serait-ce qu'en ce qui concerne l'expression des contraintes d'intégrité et le respect
des impératifs LDD, ou encore la rédaction des requêtes de manipulation des données : si l'on veut en savoir
plus, le mieux est de se lancer dans une étude approfondie de l'ouvrage de référence [Date 2003].
On y découvrira que la grande foule des contraintes et contrôles concernant spécifiquement l'historisation
des données sont automatisables et peuvent donc être rendus transparents, le système pouvant les prendre
à sa charge. Je traduis et résume ce qui est écrit page 242 de l'ouvrage :
|
« Nous conjecturons que le système est à même d'inférer ces contraintes, évitant donc à l'utilisateur
d'avoir à les définir de manière explicite. »
|
Le moyen d'y parvenir est d'élever le niveau d'abstraction (to raise the level of abstraction, page 239),
ce qui a toujours été un souci constant chez Codd, Date & Darwen, et n'est rendu possible que grâce à un
soin extrême apporté à la structure du langage, ce qui est le cas de Tutorial D.
Toujours dans [Date 2003], on découvrira
l'ensemble des impératifs LDD, le principe de généralisation des concepts (U_keys, foreign U_keys
par exemple) ;
on y trouvera de nombreux exemples de requêtes de consultation et de mise à jour des bases de données,
l'aide apportée par les relvars virtuelles (vues). Outre la U_jointure et la U_projection dont nous avons donné
un aperçu, on y trouvera encore la généralisation des opérateurs relationnels, et bien d'autres sujets en relation
avec les données temporelles et plus généralement intervallaires. La théorie relationnelle n'a pas fini d'évoluer,
et pour s'en convaincre il suffit d'explorer cette fois-ci [Date 2010], loin du train-train quotidien.
Quoi qu'il en soit, pour avoir crapahuté dans les données temporelles depuis le milieu des années soixante,
tant dans les domaines de la conception, de la programmation, de l'administration des bases de données,
de leur audit et de leur sauvetage, dans tous types d'entreprises, je me dis que si l'on avait disposé
dès le départ des travaux de
Date, Darwen et Lorentzos, il y aurait eu infiniment moins d'âneries de proférées, de temps inutilement perdu
lors du développement des applications et, il va sans dire, d'erreurs qui auraient pu être évitées, par
wagons entiers.
Vive le Modèle Relationnel de Données !
Copyright © 2008 - François de Sainte Marie.
Aucune reproduction, même partielle, ne peut être faite
de ce site ni de l'ensemble de son contenu : textes, documents, images, etc.
sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à
trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.