Developpez.com

Télécharger gratuitement le magazine des développeurs, le bimestriel des développeurs avec une sélection des meilleurs tutoriels

Developpez.com - ALM
X

Choisissez d'abord la catégorieensuite la rubrique :

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.
./images/Idea.png (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 :

./images/Carre_noir(puce)non_indente.jpgINTERVAL 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}
./images/Carre_noir(puce)non_indente.jpgTRUC (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 :

./images/Carre_noir(puce)non_indente.jpgIl 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 ? »
./images/Carre_noir(puce)non_indente.jpgIl 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

./images/Accordeon.png
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 :

./images/Carre_noir(puce)non_indente.jpgSoit 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
./images/Carre_noir(puce)non_indente.jpgSoit 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 :

  1. Décomposition par UNPACK d'un intervalle quelconque en intervalles élémentaires,

  2. 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,

  3. 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 :

  1. 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.

  2. 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) :

  1. Décomposition par UNPACK d'un intervalle quelconque en intervalles élémentaires,

  2. Exécution de l'opération relationnelle traditionnelle voulue, dans laquelle sont impliqués ces intervalles élémentaires,

  3. 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.

./images/Carre_noir(puce)non_indente.jpg 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) ;
./images/Carre_noir(puce)non_indente.jpg 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 :

./images/warning.gif 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.

./images/IndexBleu_19x30.jpg 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 :

./images/warning.gif 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 :

./images/warning.gif 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 :

./images/IndexBleu_19x30.jpg 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 :

./images/warning.gif 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 :

./images/warning.gif 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 :

./images/warning.gif 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 !

 

Valid XHTML 1.0 TransitionalValid CSS!

Copyright © 2008 - François de Sainte Marie. Aucune reproduction, même partielle, ne peut être faite de ce site et 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.

Contacter le responsable de la rubrique ALM