Précédent   Forum des professionnels en informatique > Général Développement > Conception > Modélisation
Modélisation Forum d'entraide pour les diagrammes UML et les MCD
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 18/04/2011, 17h49   #1
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Par défaut Base de données recette + historique

Bonjour,
Je cherche à concevoir une base de données « recette ».
Le procédé peut paraître commun et simple mais un grand souci d’historique se pose.
Je m’explique :
Ci-dessous un modèle conceptuel de donnée relativement simplifié :



Maintenant je veux pouvoir modifier des données, tels que:
-quantité d'un ingrédient
-ordre des ingrédients
-supprimer un ingrédient
- un ingrédient peut intervenir plusieurs fois dans une recette avec un ordre différent.

Tout en sauvegardant les anciennes valeurs.
C'est à dire que je veux pouvoir retrouver les quantités et les ingrédients que j'ai utilisé dans mes recettes sauvegardés. Donc il ne m'est pas autorisé de modifier (ni supprimer) une ligne de la BDD

Une solution fonctionnelle à cela est de créer un nouvelle table contenant des révisions de recette:



Donc je peux modifier une recette, il suffirait de faire une nouvelle révision si besoin est. C'est à dire que si j'ai besoin de modifier une quantité d'un ingrédient, je dois créer un nouvelle révision et indiquer la date à laquelle je l'ai faite. Ainsi avec cette date je pourrais retrouver mes anciennes compositions et je pourrait également affiché que les "nouvelles" recettes.

Le souci est que si une recette est constitué de 5000 ingrédients et que je m'aperçois au bout d'une semaine d'utilisation que l'ingrédient x n'est pas nécessaire, je vais devoir créer une nouvelles révision et sauvegarder à nouveau 4999 lignes identiques aux autres

Quel gaspillage

Mon modèle ne serait donc pas optimisé...
Quelqu'un aurait-il une meilleur solution ?
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 20
Vieux 20/04/2011, 02h55   #2
Expert Confirmé Sénior

 
Avatar de fsmrel
 
Spécialiste en bases de données
Inscription : septembre 2006
Messages : 2 882
Détails du profil
Informations professionnelles :
Activité : Spécialiste en bases de données
Secteur : Conseil

Informations forums :
Inscription : septembre 2006
Messages : 2 882
Points : 5 116
Points : 5 116
Bonsoir Vilukariok,


Faut pas pleurer, on devrait arriver à faire quelque chose...

Par référence à l’ouvrage de Lorentzos, Date et Darwen Temporal Data and the Relational Model, il faut isoler les données historisées des données en cours (voyez aussi le support de cours de Hugh Darwen).


J’utilise la notation E/R pour le MCD, car avec Merise, la prise en compte du temps n’est pas adaptée (nécessité d’une entité-type DATE et d’une entité-type PERIODE quand la date ou la période fait partie de l’identifiant d’une association-type, et là, y a du monde).


MCD


MLD


Observations :

Chaque attribut de la table RECETTE fait l'objet d'une table d'historisation (en l'occurrence Quantité et OrdreInsertion).

La table RECETTE ne contient que les données actives, et chacune d’elle est datée, afin de pouvoir la comparer avec son homologue en historique. La paire {RecetteId, IngredientId} est datée elle aussi (attribut RecetteDepuis).

La table RECETTE_HISTORIQUE sert à historiser les recettes dont on ne garde pas la trace dans la table RECETTE (quitte à faire revivre ces recettes un jour ou l’autre).

La table QUANTITE_HISTORIQUE est utilisée seulement pour les quantités historisées.

ORDRE_INSERTION_HISTORIQUE est utilisée seulement pour les ordres d’insertions historisés.

Les attributs RecetteDurant, QuantiteDurant et OrdreInsertionDurant sont du type INTERVAL_DATE : [di : dj] où di représente une date de début de période et dj la date de fin correspondante. Si votre SGBD ne vous permet pas de mettre en œuvre le type INTERVAL_DATE, vous utiliserez des dates de début et de fin. Si vous utilisez des timestamps plutôt que des dates, le principe est le même, avec utilisation du type INTERVAL_TIMESTAMP, mutatis mutandis.

Le but de la manœuvre est de n’historiser que ce qui doit l’être : si à telle date vous historisez une quantité, pourquoi historiser aussi le reste ? (Attention, quand vous historisez la quantité, seule la table QUANTITE_HISTORIQUE est mise à jour, la table RECETTE_HISTORIQUE n’est pas concernée).

Cette modélisation peut vous surprendre, mais simplifie grandement les opérations et permet d’être plus parcimonieux quant au stockage.

N.B. Quand vous dessinez des carrés et des ronds, merci de les faire moins grands...

Bon appétit !
__________________
_
Faites simple, mais pas plus simple ! (A. Einstein)
E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
=> La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

__________________

Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !)
fsmrel est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 20/04/2011, 09h47   #3
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Merci de votre réponse fsmrel.

Et merci égalemment du conseil pour ces livres dont vous parlez.
Je vais me renseigner sur ce livre: "Temporal Data and the Relational Model", peut être pourra-t-il m'aider à résoudre bien des soucis d'historique et de pérennité d'informations.

Je prend connaissance de votre modèle et je suis en train de le mettre en application pour y voir plus clair. Les questions fuseront dès que j'aurais saisi toutes les subtilités (qui sont encore flou pour moi ).

Déjà le principe de sauvegarder que ce qui doit l’être est très intéressant
Mais dans ce cas là une première question me viens à l'esprit:

Si je veux généraliser ce modèle et l'utiliser pour d'autre applications plus grande, je devrais utiliser une table d'historisation pour chaque paramètre qui entre dans la table active (table recette dans ce cas présent)?
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 20/04/2011, 15h25   #4
Expert Confirmé Sénior

 
Avatar de fsmrel
 
Spécialiste en bases de données
Inscription : septembre 2006
Messages : 2 882
Détails du profil
Informations professionnelles :
Activité : Spécialiste en bases de données
Secteur : Conseil

Informations forums :
Inscription : septembre 2006
Messages : 2 882
Points : 5 116
Points : 5 116
Bonjour Vilukariok,


Citation:
Envoyé par Vilukariok Voir le message
Je vais me renseigner sur ce livre: "Temporal Data and the Relational Model", peut être pourra-t-il m'aider à résoudre bien des soucis d'historique et de pérennité d'informations.
Cet ouvrage comporte plus de 400 pages, et comme le fait remarquer un commentaire client chez amazon.fr, on ne le lit pas comme un roman et il pourrait dérouter celui qui n’est pas préparé. Par ailleurs, l’essentiel figure au chapitre 23 du célèbre An Introduction to Database Systems de C.J. Date (de l’ordre de près de 800 000 mille exemplaires vendus). En tout cas, si vous avez des problèmes de compréhension, posez vos questions ici. Je ne dis pas que je saurai répondre à toutes, mais bon, « fusez » quand même.



Citation:
Envoyé par Vilukariok Voir le message
Si je veux généraliser ce modèle et l'utiliser pour d'autre applications plus grande, je devrais utiliser une table d'historisation pour chaque paramètre qui entre dans la table active (table recette dans ce cas présent)?
La règle est simple, elle est la conséquence logique des postulats de simplicité de la formulation des requêtes et de parcimonie que j’ai évoqués : toute donnée (à l’instar de la quantité) dont on veut garder la trace doit être historisée dans une table qui lui est dédiée.

N.B. Le terme paramètre ne convient pas pour une table, il faut parler d’attribut (ou de colonne dans le cas du Sorry Query Language, alias SQL)


Une certaine appréhension pourra peut-être vous saisir à l’idée de multiplier le nombre de tables : certes c’est un certain prix à payer, mais le retour sur investissement est juteux. Pour ma part, j’ai mis en place des bases de données de mille tables et plus sans problème, après urbanisation de tout ce monde-là. En effet, il ne faut pas confondre bases de données et écuries d’Augias. L'art contemporain new-yorkais envahissant n'a pas sa place ici...



Bon courage !
__________________
_
Faites simple, mais pas plus simple ! (A. Einstein)
E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
=> La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

__________________

Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !)
fsmrel est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 20/04/2011, 17h42   #5
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Citation:
Par ailleurs, l’essentiel figure au chapitre 23 du célèbre An Introduction to Database Systems de C.J. Date
Je pense que des supports écris (comme vous me le proposez) me seraient fort utile dans mon travail (Concevoir des BDD de production. Données pérennes et historisation de toutes les informations). Je ne sais que choisir entre Temporal Data and the Relational Model et An Introduction to Database Systems et peut être même encore d'autre "bible". Avez-vous une recommandation particulière?

Ceci dit, allez hop, j'urbanise:

Je me suis permis de reprendre votre schéma en modifiant le type "INTERVAL_DATE". En effet mon SGBG ne me le permet pas, je compte donc utiliser une "dateDebut" et une "dateFin".



Maintenant mon cahier des charges me dit que:
Un ingrédient peut intervenir plusieurs fois dans un recette à un ordre différent.

Deux solutions s'offrent à moi:
- Faire une clef primaire unique et autoincrementante dans la table "recette"
- Rajouter l'attribut "Ordre" en clef primaire

Pour l'instant je ne visualise pas la limite et la différence entre ces 2 options. Peut être même qu'une autre solution serait plus judicieuse?
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 21/04/2011, 19h02   #6
Expert Confirmé Sénior

 
Avatar de fsmrel
 
Spécialiste en bases de données
Inscription : septembre 2006
Messages : 2 882
Détails du profil
Informations professionnelles :
Activité : Spécialiste en bases de données
Secteur : Conseil

Informations forums :
Inscription : septembre 2006
Messages : 2 882
Points : 5 116
Points : 5 116
Bonsoir Vilukariok,


Citation:
Envoyé par Vilukariok Voir le message
Avez-vous une recommandation particulière ?
Commencez par la bible, à savoir An Introduction to Database Systems( 8th edition)


Citation:
Envoyé par Vilukariok Voir le message
Maintenant mon cahier des charges me dit que:
Un ingrédient peut intervenir plusieurs fois dans une recette à un ordre différent.
Si je comprends bien, une recette peut donner lieu à des variantes, par exemple :

Si le nom de la recette est « Le poulet à la fsmrel » et que l’on utilise d’abord l’ingrédient crème chantilly puis l’ingrédient mayonnaise, on aura la recette r1 (pour laquelle la quantité est q1) alors que pour la variante r2 de ce même poulet à la fsmrel on utilisera d’abord la mayonnaise avant la crème chantilly (la quantité peut rester q1 ou devenir q2). C’est bien cela ?


Citation:
Envoyé par Vilukariok Voir le message
Deux solutions s'offrent à moi :
- Faire une clef primaire unique et autoincrementante dans la table "recette"
- Rajouter l'attribut "Ordre" en clef primaire.
Si vous définissez un attribut supplémentaire, appelons-le Rid qui servira pour la nouvelle clé primaire, vous ne pourrez pas faire l’économie de la clé alternative {RecetteId, IngredientId, Ordre} car elle correspond à une règle de gestion qu’il faut garantir. Les choses risquent de se compliquer dans la gestion des opérations en relation avec les historiques (il faudra gérer des clés dans lesquelles, outre les dates, interviennent Rid d’une part et RecetteId, IngredientId, Ordre d’autre part) : l’attribut Rid ne m’inspire pas particulièrement, on va essayer de s’en passer.


Avant de traiter de l’évolution du modèle, ce dont nous parlerons plus tard, j’ai des remarques à faire concernant ce que nous avons vu jusqu’ici.


Citation:
Envoyé par Vilukariok Voir le message
Je me suis permis de reprendre votre schéma [...]
Je vous en prie, il est fait pour ça...
Si donc je me suis planté, je ne serai pas seul à prendre un savon...


Citation:
Envoyé par Vilukariok Voir le message
en modifiant le type "INTERVAL_DATE". En effet mon SGBG ne me le permet pas, je compte donc utiliser une "dateDebut" et une "dateFin".
Ach ! Ces SGBD puissants, souples, conviviaux, etc. ..., au fait quel est le vôtre ?

Affaiblissons donc le système et simulons les périodes à coup de paires de dates de début et de fin.

Le MCD que je vous propose diffère légèrement du vôtre en ce qui concerne les identifiants.

En effet, si l’on prend par exemple le cas de la table RECETTE_HISTORIQUE, vous avez défini une clé primaire :
{RecetteId, IngredientId, RecetteDurantDeb, RecetteDurantFin}
Laquelle est réductible à :
{RecetteId, IngredientId, RecetteDurantDeb}
Et pour ne pas faire de jaloux, et pour des raisons de symétrie, on peut définir une clé alternative :
{RecetteId, IngredientId, RecetteDurantFin}
Pour ces histoires de différents types de clés et de leur réductibilité, il serait peut-être bon que vous relisiez l’article Bases de données relationnelles et normalisation, à partir du paragraphe 3.2.2.

Ceci dit, ces clés ayant des dates entrant dans leur composition ne garantissent rien... En effet, si par exemple pour la recette r01 et l’ingrédient 01, l’attribut RecetteDurantDeb de la table RECETTE_HISTORIQUE prend la valeur 2001-01-05 et l’attribut RecetteDurantFin prend la valeur 2001-01-15, nous ne pouvons pas empêcher l’ajout dans cette table d’un tuple du genre :
<r01, i01, 2001-01-06, 2001-01-17>
C’est une vraie passoire.

Premières contraintes élémentaires à prévoir :

Il faut s’assurer que la date de fin d’une période donnée n’est pas antérieure à la date de début de cette période. C’est le rôle de la contrainte RECETTE_HISTORIQUE_CHK01 ci-dessous :

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE TABLE RECETTE_HISTORIQUE 
(
   RecetteId            CHAR(04)             NOT NULL,
   IngredientId         CHAR(04)             NOT NULL,
   RecetteDurantDeb     DATETIME             NOT NULL,
   RecetteDurantFin     DATETIME             NOT NULL,
   CONSTRAINT RECETTE_HISTORIQUE_PK PRIMARY KEY  (RecetteId, IngredientId, RecetteDurantDeb),
   CONSTRAINT RECETTE_HISTORIQUE_AK UNIQUE (RecetteId, IngredientId, RecetteDurantFin),
   CONSTRAINT RECETTE_HISTORIQUE_INGREDIENT_FK FOREIGN KEY (IngredientId)
      REFERENCES INGREDIENT (IngredientId),
   CONSTRAINT RECETTE_HISTORIQUE_RECETTE_NOM_FK FOREIGN KEY (RecetteId)
      REFERENCES RECETTE_NOM (RecetteId),
   CONSTRAINT RECETTE_HISTORIQUE_CHK01 CHECK (NOT RecetteDurantFin < RecetteDurantDeb)
) ;
J’utilise ici SQL Server 2005. Pour faciliter la mise en œuvre des exemples, les attributs RecetteId et IngredientId sont du type CHAR(04). Comme le type TIMESTAMP vu par SQL Server n’est pas conforme à la norme, je me suis rabattu sur le type DATETIME qui suffit largement.

Comme on l’a vu (effet passoire), Il faut s’assurer que les dates ne se chevaucheront pas. Un trigger pour les INSERT conviendra :

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE TRIGGER RECETTE_HISTORIQUE_T1 ON RECETTE_HISTORIQUE INSTEAD OF INSERT AS
 SELECT  ''   
 FROM    INSERTED AS x INNER JOIN RECETTE_HISTORIQUE AS y 
         ON   x.RecetteId = y.RecetteId 
         AND  x.IngredientId = y.IngredientId
         AND (x.RecetteDurantDeb < y.RecetteDurantDeb  AND x.RecetteDurantFin >= y.RecetteDurantDeb 
          OR  x.RecetteDurantDeb >= y.RecetteDurantDeb AND x.RecetteDurantDeb <= y.RecetteDurantFin)
;
 IF @@Rowcount > 0
      BEGIN 
         SELECT 'Insert dans RECETTE_HISTORIQUE : Recouvrement de période', * FROM INSERTED
         RAISERROR ('Insert dans RECETTE_HISTORIQUE : Recouvrement de période',16,1) 
         RETURN
      END
 
 INSERT INTO RECETTE_HISTORIQUE 
             SELECT  *
             FROM    INSERTED

Et s’il s’avère nécessaire de modifier les dates historisées (ce qui en principe ne devrait pas se produire), il faudra prévoir un trigger de type INSTEAD OF UPDATE, toujours pour se prémunir contre les recouvrements de périodes.

Suite au prochain épisode. Et n'abusez pas de la crème chantilly à la mayonnaise...
__________________
_
Faites simple, mais pas plus simple ! (A. Einstein)
E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
=> La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

__________________

Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !)
fsmrel est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 22/04/2011, 14h44   #7
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Citation:
Si je comprends bien, une recette peut donner lieu à des variantes, par exemple :

Si le nom de la recette est « Le poulet à la fsmrel » et que l’on utilise d’abord l’ingrédient crème chantilly puis l’ingrédient mayonnaise, on aura la recette r1 (pour laquelle la quantité est q1) alors que pour la variante r2 de ce même poulet à la fsmrel on utilisera d’abord la mayonnaise avant la crème chantilly (la quantité peut rester q1 ou devenir q2). C’est bien cela ?
Exactement, vous avez tous compris.

Je peux même rajouter qu'il est possible que l'on décide que la variante r2 manque de sauce mayonnaise. Donc que la recette r2 soit finalement faite dans cet ordre précisément:
mayonnaise / chantilly / œuf / mayonnaise / poivre

On peux égalemment s'apercevoir un jour que ce n'est pas très digest.Et donc finalement qu'il faut enlever l'œuf. Ce qui donne:

mayonnaise / chantilly / mayonnaise / poivre

d'où le besoin d'un attribut supplémentaire dans la clef primaire.

Citation:
Ach ! Ces SGBD puissants, souples, conviviaux, etc. ..., au fait quel est le vôtre ?
L'heureux élue est: ... MSQL 4.1

Ce qui me bloque un peu pour l'application de la fin de votre message.
Mais ce qui ne m'empêche pas de comprendre ce qui y est dit. Je suis deja plongé dedans
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 25/04/2011, 01h31   #8
Expert Confirmé Sénior

 
Avatar de fsmrel
 
Spécialiste en bases de données
Inscription : septembre 2006
Messages : 2 882
Détails du profil
Informations professionnelles :
Activité : Spécialiste en bases de données
Secteur : Conseil

Informations forums :
Inscription : septembre 2006
Messages : 2 882
Points : 5 116
Points : 5 116
Bonsoir Vilukariok,


Citation:
Envoyé par Vilukariok Voir le message
L'heureux élu est: ... MSQL 4.1
Ne serait-ce pas plutôt MySQL 4.1 ? Si oui, il serait préférable de passer à une version postérieure...
Par ailleurs, vous pouvez toujours vous exercer avec SQL Server Express (gratuit), comme je le fais moi-même, bien que ce ne soit pas mon SGBD habituel.


Citation:
Envoyé par Vilukariok Voir le message
d'où le besoin d'un attribut supplémentaire dans la clef primaire.
Exact, et cet attribut serait donc OdreInsertion...

Dans cette optique, la table ORDRE_INSERTION_HISTORIQUE est absorbée par la table RECETTE_HISTORIQUE.

Comme j’ai des problèmes pour afficher les images, je passe directement en mode SQL. Je rappelle que normalement les attributs participant à une clé primaire sont plutôt du type INTEGER, mais pour tester il m’est plus facile de passer au type CHAR.

Tables RECETTE_NOM et INGREDIENT :

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE RECETTE_NOM 
(
   RecetteId            CHAR(04)             NOT NULL,
   RecetteNom           VARCHAR(48)          NOT NULL,
   CONSTRAINT RECETTE_NOM_PK PRIMARY KEY  (RecetteId)
) ;
CREATE TABLE INGREDIENT 
(
   IngredientId         CHAR(04)             NOT NULL,
   IngredientNom        VARCHAR(48)          NOT NULL,
   CONSTRAINT INGREDIENT_PK PRIMARY KEY  (IngredientId)
) ;

Table RECETTE :

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE RECETTE (
   RecetteId            CHAR(04)             NOT NULL,
   IngredientId         CHAR(04)             NOT NULL,
   OrdreInsertion       CHAR(04)             NOT NULL,
   RecetteDepuis        DATETIME             NOT NULL,
   QuantiteDepuis       DATETIME             NOT NULL,
   Quantite             INT                  NOT NULL,
   CONSTRAINT RECETTE_PK PRIMARY KEY  (RecetteId, IngredientId, OrdreInsertion),
   CONSTRAINT RECETTE_RECETTE_NOM_FK FOREIGN KEY (RecetteId)
      REFERENCES RECETTE_NOM (RecetteId),
   CONSTRAINT RECETTE_INGREDIENT_FK FOREIGN KEY (IngredientId)
      REFERENCES INGREDIENT (IngredientId) 
) ;

Table RECETTE_HISTORIQUE :

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE RECETTE_HISTORIQUE 
(
   RecetteId            CHAR(04)             NOT NULL,
   IngredientId         CHAR(04)             NOT NULL,
   OrdreInsertion       CHAR(04)             NOT NULL,
   RecetteDurantDeb     DATETIME             NOT NULL,
   RecetteDurantFin     DATETIME             NOT NULL,
   CONSTRAINT RECETTE_HISTORIQUE_PK PRIMARY KEY  (RecetteId, IngredientId, OrdreInsertion, RecetteDurantDeb),
   CONSTRAINT RECETTE_HISTORIQUE_AK UNIQUE (RecetteId, IngredientId, OrdreInsertion, RecetteDurantFin),
   CONSTRAINT RECETTE_HISTORIQUE_INGREDIENT_FK FOREIGN KEY (IngredientId)
      REFERENCES INGREDIENT (IngredientId),
   CONSTRAINT RECETTE_HISTORIQUE_RECETTE_NOM_FK FOREIGN KEY (RecetteId)
      REFERENCES RECETTE_NOM (RecetteId),
   CONSTRAINT RECETTE_HISTORIQUE_CHK01 CHECK (NOT RecetteDurantFin < RecetteDurantDeb)
) ;

Table QUANTITE_HISTORIQUE :

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE QUANTITE_HISTORIQUE (
   RecetteId            CHAR(04)             NOT NULL,
   IngredientId         CHAR(04)             NOT NULL,
   OrdreInsertion       CHAR(04)             NOT NULL,
   QuantiteDurantDeb    DATETIME             NOT NULL,
   QuantiteDurantFin    DATETIME             NOT NULL,
   Quantite             INT                  NOT NULL,
   CONSTRAINT QUANTITE_HISTORIQUE_PK PRIMARY KEY  (RecetteId, IngredientId, OrdreInsertion, QuantiteDurantDeb),
   CONSTRAINT AK_CLE_2_QUANTITE UNIQUE (RecetteId, IngredientId, OrdreInsertion, QuantiteDurantFin),
   CONSTRAINT QUANTITE_HISTORIQUE_INGREDIENT_FK FOREIGN KEY (IngredientId)
      REFERENCES INGREDIENT (IngredientId),
   CONSTRAINT QUANTITE_HISTORIQUE_RECETTE_NOM_FK FOREIGN KEY (RecetteId)
      REFERENCES RECETTE_NOM (RecetteId)
   CONSTRAINT QUANTITE_HISTORIQUE_CHK01 CHECK (NOT QuantiteDurantFin < QuantiteDurantDeb)
) ;
Avant d’aller plus avant (on n'a traité que des amuse-gueule, attention au plat de résistance... ), ces définitions de tables vous conviennent-elles ?
__________________
_
Faites simple, mais pas plus simple ! (A. Einstein)
E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
=> La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

__________________

Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !)
fsmrel est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/04/2011, 11h35   #9
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Bonjour,

Citation:
Envoyé par Vilukariok Voir le message
L'heureux élu est: ... MSQL 4.1
Oups, excusez-moi, j'ai oublié un "y".
Vous aviez bien compris: il s'agit de MySQL 4.1

Citation:
Si oui, il serait préférable de passer à une version postérieure...
Hélas cette démarche n'est pas si évidente (serveur qui tourne 24h/24h) mais nous sommes en train de prévoir une prochaine migration sur MySQL 5.1

Citation:
ces définitions de tables vous conviennent-elles ?
Je me penche dessus et effectue quelques test afin de mieux répondre à la question.

Mais déjà, merci beaucoup pour votre aide.
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/04/2011, 16h51   #10
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Citation:
ces définitions de tables vous conviennent-elles ?
Entièrement d'accord.

Au champ 'char(04)' et 'datetime' près :



Petite question-remarque:

1) Les 2 tables d'historisation ne sont remplies que lorsqu'une recette change?
En effet, je ne sais pas si il y a une utilité à pré remplir les tables d'historisation en laissant vide les champs "Fin".
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/04/2011, 00h45   #11
Expert Confirmé Sénior

 
Avatar de fsmrel
 
Spécialiste en bases de données
Inscription : septembre 2006
Messages : 2 882
Détails du profil
Informations professionnelles :
Activité : Spécialiste en bases de données
Secteur : Conseil

Informations forums :
Inscription : septembre 2006
Messages : 2 882
Points : 5 116
Points : 5 116
Bonsoir Vilukariok,


Nous sommes en phase en ce qui concerne le diagramme (MLD).


Citation:
Envoyé par Vilukariok Voir le message
Les 2 tables d'historisation ne sont remplies que lorsqu'une recette change?
Oui. Exemples de scénarios :

1er cas de figure :

Supposons qu’une recette (table RECETTE) ait la valeur suivante (pour mon confort personnel, je passe en CHAR(04) ) :
RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
RecetteDepuis = '2000-01-15'
QuantiteDepuis = '2000-01-15'
Quantite = 100
Et que la quantité passe à 200 à la date du '2000-04-22' :

On historise donc et la table QUANTITE_HISTORIQUE va héberger la ligne suivant :
RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
QuantiteDurantDeb = '2000-01-15'
QuantiteDurantFin = '2000-01-21' _____________ coquille de ma part, lire : '2000-04-21'
Quantite = 100
Tandis qu’en échange, dans la table RECETTE on aura la ligne suivante :
RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
RecetteDepuis = '2000-04-15' ________________ coquille de ma part, lire : '2000-01-15'
QuantiteDepuis = '2000-04-22'
Quantite = 200
La recette < 'r01', 'i01', 'o01'> existe toujours, simplement on trace la modification des quantités.


Deuxième cas de figure :

C’est l’attribut Ordre qui change de valeur. Considérons la recette :
RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
RecetteDepuis = '2000-01-15'
QuantiteDepuis = '2000-01-15'
Quantite = 100
Et supposons qu’elle soit remplacée le '2000-07-14', par la nouvelle recette 'r01', 'i01', 'o02'>. La recette d’ordre 'o01' peut être conservée telle quelle dans la table RECETTE si on la considère comme étant finalement toujours active, ou bien on peut préférer l’expulser dans la table RECETTE_HISTORIQUE (je rappelle que la table RECETTE exprime ce qui est actif et n’a pas intérêt à être surchargée par des données mortes qui la rendraient obèse). La table RECETTE_HISTORIQUE accueille alors la ligne suivante :
RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
RecetteDurantDeb = '2000-01-15'
RecetteDurantFin = '2000-07-13'
La trace de la quantité sera évacuée dans la table QUANTITE_HISTORIQUE :
RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
QuantiteDurantDeb = '2000-01-15'
QuantiteDurantFin = '2000-07-13'
Quantite = 100
Tandis que la nouvelle recette viendra remplacer l’ancienne dans la table RECETTE :
RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o02'
RecetteDepuis = '2000-07-14'
QuantiteDepuis = '2000-07-14'
Quantite = 500 (ou l’ancienne valeur 100, c’est vous qui voyez).
En tout cas, une chose est sûre : tout ce qui part en historisation a forcément une date de fin. Quand je lis ceci :

Citation:
Envoyé par Vilukariok Voir le message
je ne sais pas si il y a une utilité à pré remplir les tables d'historisation en laissant vide les champs "Fin".
Je tremble en vous lisant (horresco referens comme dit l'autre)...

Laisser « vides » les dates de fin est contraire au principe même de l’historisation où l’on ne doit raisonner qu’en intervalles (ou périodes si vous préférez). Si vous scrutez attentivement les structures de tables que je vous ai proposées, vous constaterez que chaque attribut est qualifié NOT NULL, sans exception.


Pour en revenir au plat de résistance :

Avec le trigger que je vous avais proposé, vous aviez pu constater que le recouvrement (ou chevauchement) d’intervalles était interdit. Pour aller plus avant, voici les règles de base de l’historisation quant aux contraintes auxquelles celle-ci doit se plier (elles font partie des nine requirements définis par Date, Darwen et Lorentzos) :
1) Si la base de données montre qu’il y a une recette r au jour j, elle ne doit contenir qu’un seul tuple qui exprime ce fait.
Ainsi, si la table RECETTE dit que la recette existe au jour j, la table RECETTE_HISTORIQUE doit être muette à ce sujet, tandis que si la table RECETTE_ HISTORIQUE dit que la recette existe au jour j, c’est la table RECETTE qui doit être muette.

N.B. En relationnel, un tuple est ce que l’on appelle une ligne de table en SQL.

2) Si la base de données montre qu’il y a une recette r à j et j+1, elle ne doit contenir qu’un seul tuple qui exprime ce fait. Je vous laisse démontrer le bien-fondé de cette contrainte.

3) Si la base de données montre qu’il y a une recette r au jour j (table RECETTE ou table RECETTE_HISTORIQUE), elle doit aussi montrer qu’il existe un tuple qui exprime une quantité au jour j pour cette recette (table RECETTE ou table QUANTITE_HISTORIQUE).

4) Si la base de données montre qu’il existe un tuple qui exprime une quantité au jour j pour la recette r (table RECETTE ou table QUANTITE_HISTORIQUE), elle ne doit contenir qu’un seul tuple qui exprime ce fait.

5) Si la base de données montre qu’il existe un tuple qui exprime une quantité à j et j+1 pour la recette r (table RECETTE ou table QUANTITE_HISTORIQUE), elle ne doit contenir qu’un seul tuple qui exprime ce fait.

6) Si la base de données montre qu’il existe un tuple qui exprime une quantité au jour j pour la recette r (table RECETTE ou table QUANTITE_HISTORIQUE), elle doit aussi montrer qu’il existe un tuple au jour j pour cette recette (table RECETTE ou table RECETTE_HISTORIQUE).
Il existe d’autres contraintes, mais celles que j’ai énumérées sont incontournables.

Par ailleurs, vous pourriez préférer que la table RECETTE_HISTORIQUE soit absorbée par la table QUANTITE_HISTORIQUE, pour cause de double emploi apparent. Mais attention, les intervalles [RecetteDurantDeb:RecetteDurantDeb] et [QuantiteDurantDeb:QuantiteDurantDeb] vivent a priori chacun sa vie. Par ailleurs, que faire le jour où, en plus de l’attribut Quantite, vous définiriez un attribut supplémentaire pour les recettes (la qualité recherchée, l’inventeur de la chose ou que sais-je) ? Vous seriez coincé et obligé de bien réfléchir à toutes les conséquences sur les applications déjà développées d'un retour à l'architecture que je vous ai proposée. La table RECETTE_HISTORIQUE est une sorte de tour de contrôle pour l'historisation.

En tout cas, les six contraintes qui précèdent font l’objet de triggers intéressants (je vous laisse réfléchir un peu au pseudo-code correspondant), il y a un prix à payer pour que la base de données ne finisse pas par ressembler à n’importe quoi (quand on voit ce qu'on voit...)

A suivre...
__________________
_
Faites simple, mais pas plus simple ! (A. Einstein)
E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
=> La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

__________________

Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !)
fsmrel est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/04/2011, 11h10   #12
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Bonjour fsmrel,

Tout d'abord, merci encore pour toutes ces informations, en plus de me guider et me conseiller la bonne façon pour résoudre mon problème, j'apprends une quantité d'information non négligeable. Merci!

Ensuite, je me permet de revenir sur un de vos propos:

Citation:
Et que la quantité passe à 200 à la date du '2000-04-22' :
On historise donc et la table QUANTITE_HISTORIQUE va héberger la ligne suivant :

RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
QuantiteDurantDeb = '2000-01-15'
QuantiteDurantFin = '2000-01-21'
Quantite = 100

Tandis qu’en échange, dans la table RECETTE on aura la ligne suivante :

RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
RecetteDepuis = '2000-04-15'
QuantiteDepuis = '2000-04-22'
Quantite = 200
Vouliez-vous dire:
QuantiteDurantFin = '2000-04-21' ? et
RecetteDepuis = '2000-01-15' ?

Sinon je suis un peu perdu...

Ensuite pour le deuxième cas de figure, je suis entièrement d'accord avec vous.
Cependant si je me projette dans l'avenir, je vois que récupérer les données correspondants à une date antérieur ne vas pas être un exercice simple.

Prenons comme exemple toujours votre deuxième cas de figure:
La fameuse recette que vous voulez changer contient 50 ingrédients.
Je pars du principe que le changement de l'ordre a été effectué (tel que vous l'avez fait). Votre changement correspondait par exemple à déplacer un constituant pour le mettre en dernière position dans la recette.

Je dois maintenant retrouver la recette qui à été utilisé le '2000-06-01'.
Diantre.. Ces informations sont dispatchées dans ces 3 tables...

Il va donc falloir que je fasse une requête sur l'ensemble de mes tables: RECETTE , RECETTE_HISTORIQUE et QUANTITE_HISTORIQUE.
Cette requête va peut-être être du type JOIN ou UNION ou les 2? Ou alors il est peut être plus judicieux de déplacer entièrement la recette dans les 2 tables d'historisation lors d'un tel changement?

Et enfin: Je suis tout à fait d'accord avec ces 'nine requirements' .Cela montre bien le fonctionnement de cette future BDD. Je pense que cet exemple de BDD 'recette' va m'être très utile pour toutes mes futures applications.

Merci.

Merci
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/04/2011, 15h24   #13
Expert Confirmé Sénior

 
Avatar de fsmrel
 
Spécialiste en bases de données
Inscription : septembre 2006
Messages : 2 882
Détails du profil
Informations professionnelles :
Activité : Spécialiste en bases de données
Secteur : Conseil

Informations forums :
Inscription : septembre 2006
Messages : 2 882
Points : 5 116
Points : 5 116
Bonjour Vilukariok,


Citation:
Envoyé par Vilukariok Voir le message
je me permet de revenir sur un de vos propos :
Et que la quantité passe à 200 à la date du '2000-04-22' :
On historise donc et la table QUANTITE_HISTORIQUE va héberger la ligne suivant :

RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
QuantiteDurantDeb = '2000-01-15'
QuantiteDurantFin = '2000-01-21'
Quantite = 100

Tandis qu’en échange, dans la table RECETTE on aura la ligne suivante :

RecetteId = 'r01'
IngredientId= 'i01'
Ordre = 'o01'
RecetteDepuis = '2000-04-15'
QuantiteDepuis = '2000-04-22'
Quantite = 200
Il y a de ma part un copier/coller qui manifestement a mal fonctionné, j’ai les yeux qui ont dû se croiser (peut-être ai-je abusé en testant une recette de cocktail autant bizarre que corsé ? )

Il est évident que l’attribut RecetteDepuis de la table RECETTE n’est pas concerné par la modification de la quantité et que sa valeur reste égale à '2000-01-15'.
De même, l’attribut QuantiteDurantFin de la table QUANTITE_HISTORIQUE prend bien entendu la valeur '2000-04-21'.

En tout cas, on voit que vous suivez . Pour ma part, je mets à jour le message incriminé.

Voilà pour un premier temps. Je vais maintenant regarder la suite de votre message.
__________________
_
Faites simple, mais pas plus simple ! (A. Einstein)
E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
=> La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

__________________

Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !)
fsmrel est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/04/2011, 17h37   #14
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Citation:
En tout cas, on voit que vous suivez
Plus que jamais .

Lors de mon dernier message, j'ai aborder le faite de retrouver une recette utilisé à une date antérieur:

Je pense qu'il me faudra donc utiliser une requete de ce type:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT rec.recetteId,
rec.IngredientId, rec.Quantite,
rec.OrdreInsertion
FROM recette as rec
where rec.RECETTEdepuis < '2010-01-02 10:00:00'
and rec.quantitedepuis < '2010-01-02 10:00:00'

UNION

SELECT qh.recetteId,
qh.IngredientId, qh.Quantite,
qh.OrdreInsertion
FROM quantite_historique as qh
WHERE QuantiteDurantDeb < '2010-01-02 10:00:00'
AND quantiteDurantFin > '2010-01-02 10:00:00'

order by ordreInsertion
Je m'aperçois donc que ce n'est pas la peine de regarder dans la table recette_historique... Ceci est due au faite que l'ordre est dans ma clef primaire pour ces 2 tables d'historisation.

Donc à priori c'est normal?
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/04/2011, 01h02   #15
Expert Confirmé Sénior

 
Avatar de fsmrel
 
Spécialiste en bases de données
Inscription : septembre 2006
Messages : 2 882
Détails du profil
Informations professionnelles :
Activité : Spécialiste en bases de données
Secteur : Conseil

Informations forums :
Inscription : septembre 2006
Messages : 2 882
Points : 5 116
Points : 5 116
Bonjour Vilukariok,


Citation:
Envoyé par Vilukariok Voir le message
Je dois maintenant retrouver la recette qui à été utilisée le '2000-06-01'.
Diantre.. Ces informations sont dispatchées dans ces 3 tables...
Cette requête va peut-être être du type JOIN ou UNION ou les 2? Ou alors il est peut être plus judicieux de déplacer entièrement la recette dans les 2 tables d'historisation lors d'un tel changement?
On ne déplace rien ! Que diraient Date, Darwen et Lorentzos s’ils apprenaient ça ? Horresco referens !
L’opérateur UNION est évidemment bien mis à contribution (les ensembles étant ici disjoints, sa version UNION ALL est ici intéressante pour éviter des tris inutiles).

Si vous cherchez seulement la (les) recette(s) sur le seul critère de la date à partir de laquelle la recette à cours, et si la quantité ne vous intéresse pas, la requête ci-dessous suffit (ne vous préoccupez pas de l’opérateur CONVERT qui permet en SQL Server la conversion de type, à l’instar de CAST) :

Code sql :
1
2
3
4
5
6
7
SELECT RecetteId, IngredientId, OrdreInsertion, RecetteDepuis AS Début, 'en cours' AS Fin
FROM   RECETTE
WHERE  RecetteDepuis <= '2000-01-16'
UNION
SELECT RecetteId, IngredientId, OrdreInsertion, RecetteDurantDeb, CONVERT(CHAR(23), RecetteDurantFin, 121)
FROM   RECETTE_HISTORIQUE
WHERE  '2000-01-16' BETWEEN RecetteDurantDeb AND RecetteDurantFin ;
Si c’est plutôt la quantité qui vous intéresse :

Code sql :
1
2
3
4
5
6
7
SELECT RecetteId, IngredientId, OrdreInsertion, RecetteDepuis AS Début, 'en cours' AS Fin, Quantite
FROM   RECETTE
WHERE  RecetteDepuis <= '2000-01-16'
UNION
SELECT RecetteId, IngredientId, OrdreInsertion, QuantiteDurantDeb, CONVERT(CHAR(23), QuantiteDurantFin, 121), Quantite
FROM   QUANTITE_HISTORIQUE
WHERE  '2000-01-16' BETWEEN QuantiteDurantDeb AND QuantiteDurantFin ;
Si tout vous intéresse, date de la recette et quantité, la requête devrait ressembler à quelque chose du genre :

Code sql :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT RecetteId, IngredientId, OrdreInsertion, 
       RecetteDepuis AS 'Recette Début', 'en cours' AS 'Recette Fin', 
       QuantiteDepuis AS 'Quantité Début', 'en cours' AS 'Quantité Fin', Quantite 
FROM   RECETTE
WHERE  RecetteDepuis <= '2001-01-16'
UNION
SELECT x.RecetteId, x.IngredientId, x. OrdreInsertion, 
       x.RecetteDurantDeb, CONVERT(CHAR(23), x.RecetteDurantFin, 121),
       y.QuantiteDurantDeb, CONVERT(CHAR(23), y.QuantiteDurantFin,121), y.Quantite
FROM   RECETTE_HISTORIQUE AS x JOIN QUANTITE_HISTORIQUE AS y
       ON  x.RecetteId = y.RecetteId
       AND x.IngredientId = y.IngredientId
       AND x. OrdreInsertion = y. OrdreInsertion
       AND '2000-01-16' BETWEEN x.RecetteDurantDeb AND x.RecetteDurantFin
       AND '2000-01-16' BETWEEN y.QuantiteDurantDeb AND y.QuantiteDurantFin ;

Ou encore d'autres variations sur le thème...


Citation:
Envoyé par Vilukariok Voir le message
Lors de mon dernier message, j'ai aborder le faite de retrouver une recette utilisé à une date antérieure :
Je pense qu'il me faudra donc utiliser une requete de ce type:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT rec.recetteId,
rec.IngredientId, rec.Quantite,
rec.OrdreInsertion
FROM recette as rec
where rec.RECETTEdepuis < '2010-01-02 10:00:00'
and rec.quantitedepuis < '2010-01-02 10:00:00'
UNION
SELECT qh.recetteId,
qh.IngredientId, qh.Quantite,
qh.OrdreInsertion
FROM quantite_historique as qh
WHERE QuantiteDurantDeb < '2010-01-02 10:00:00'
AND quantiteDurantFin > '2010-01-02 10:00:00'

order by ordreInsertion
Je m'aperçois donc que ce n'est pas la peine de regarder dans la table recette_historique... Ceci est due au faite que l'ordre est dans ma clef primaire pour ces 2 tables d'historisation.
Donc à priori c'est normal?
Tout dépend de ce que vous voulez précisément exprimer. Concernant la partie historisée, vous jouez seulement sur la période [QuantiteDurantDeb:QuantiteDurantFin], on est ici dans la logique de ma 2e requête ; mais, selon vos besoins, il peut aussi se produire que vous ayez à vous intéresser à la période [RecetteDurantDeb:RecetteDurantFin], comme dans ma 1re et ma 3e version de requêtes.


P.-S. J'espère ne pas avoir commis de coquilles avec les copier/coller...
__________________
_
Faites simple, mais pas plus simple ! (A. Einstein)
E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
=> La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

__________________

Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !)
fsmrel est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/04/2011, 17h49   #16
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Je suis entièrement d'accord avec vos requêtes, je suis arrivé aux mêmes conclusions que vous, cela me réconforte dans mon projet

Citation:
P.-S. J'espère ne pas avoir commis de coquilles avec les copier/coller...
J'ai juste un petit doute sur cette ligne:

Citation:
Si c’est plutôt la quantité qui vous intéresse :
Code :
1
2
3
4
5
6
7
8
SELECT RecetteId, IngredientId, OrdreInsertion, RecetteDepuis AS Début, 'en cours' AS Fin, Quantite
FROM   RECETTE
WHERE  RecetteDepuis <= '2000-01-16'
UNION
SELECT RecetteId, IngredientId, OrdreInsertion, QuantiteDurantDeb, CONVERT(CHAR(23), QuantiteDurantFin, 121), Quantite
FROM   QUANTITE_HISTORIQUE
WHERE  '2000-01-16' BETWEEN QuantiteDurantDeb AND QuantiteDurantFin ;
J'aurais écris la même requête que vous mais en changeant "RecetteDepuis" par "QuantiteDepuis"

Du coup je ne sais que penser...
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/04/2011, 18h00   #17
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Merci encore fsmrel pour tous ces conseils.

J'ai juste une petite remarque:

Si j'ai une recette de 500 ingrédients.

Que je m'aperçois qu'il faut placer un nouvel ingrédient en position 200.

Je doit donc modifier 300 lignes (uplets).
Il faut commencer par sauvegarder dans "recette_historique" et "quantite_historique" ces 300 lignes.

Puis rajouter la nouvelle ligne qui correspond au nouvel ingrédient dans "preparation"

Et enfin modifier les 300 lignes (décaler OrdreInsertion de +1) de "recette"

Cela fait beaucoup de données.
Aurais-je intérêt à concevoir d'une manière différente l'ordre?
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/04/2011, 03h43   #18
Expert Confirmé Sénior

 
Avatar de fsmrel
 
Spécialiste en bases de données
Inscription : septembre 2006
Messages : 2 882
Détails du profil
Informations professionnelles :
Activité : Spécialiste en bases de données
Secteur : Conseil

Informations forums :
Inscription : septembre 2006
Messages : 2 882
Points : 5 116
Points : 5 116
Bonsoir Vilukariok,


Citation:
Envoyé par Vilukariok Voir le message
J'aurais écris la même requête que vous mais en changeant "RecetteDepuis" par "QuantiteDepuis"
Si l’on ne s’intéresse qu’à la quantité, c’est vrai. Mais la question initiale porte sur la recette proprement dite, la quantité ne jouant que le rôle de « bonus ». Autrement dit, il faudrait que vous réfléchissiez aux conséquences du choix de l’attribut utilisé (RecetteDepuis ou QuantiteDepuis).


Citation:
Envoyé par Vilukariok Voir le message
Aurais-je intérêt à concevoir d'une manière différente l'ordre ?
Oui, car il est hors de question de modifier des tombereaux de lignes !

Pour la table RECETTE, vous avez défini un attribut OrdreInsertion explicite. Pour être minimaliste dans les mises à jour, à savoir insérer une recette entre deux recettes, vous pourriez vous orienter vers la mise en œuvre d’une chaîne.

Cette chaîne ferait l’objet d’une table, appelons-la MAILLON. Dans le diagramme ci-dessous, l’attribut OrdreInsertion disparaît, au bénéfice de la nouvelle table.

La structure de la table RECETTE changerait du fait de la disparition de l’attribut OrdreInsertion et de l'ajout d'un attribut, appelons-le RecetteSeq, ayant pour objet lui aussi de permettre d’avoir des variantes pour la paire {RecetteId, IngredientId}, mais sans qu’il joue de rôle fonctionnel : peu importe les valeurs dont on l’affecte. Maintenant, si OrdreInsertion ne joue pas vraiment de rôle fonctionnel, on le conserve et RecetteSeq n'a pas lieu d'être.

Pour ne pas surcharger, je ne fais pas figurer ici les tables dévolues à l’historisation (qui évoluent, dans la mesure où l’attribut OrdreInsertion y est à remplacer par l’attribut RecetteSeq).




En SQL :

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE RECETTE_NOM 
(
   RecetteId            CHAR(04)             NOT NULL,
   RecetteNom           VARCHAR(48)          NOT NULL,
   CONSTRAINT RECETTE_NOM_PK PRIMARY KEY  (RecetteId)
) ;
CREATE TABLE INGREDIENT 
(
   IngredientId         CHAR(04)             NOT NULL,
   IngredientNom        VARCHAR(48)          NOT NULL,
   CONSTRAINT INGREDIENT_PK PRIMARY KEY  (IngredientId)
) ;

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE RECETTE (
   RecetteId            CHAR(04)             NOT NULL,
   IngredientId         CHAR(04)             NOT NULL,
   RecetteSeq           CHAR(04)             NOT NULL,
   RecetteDepuis        DATETIME             NOT NULL,
   QuantiteDepuis       DATETIME             NOT NULL,
   Quantite             INT                  NOT NULL,
   CONSTRAINT RECETTE_PK PRIMARY KEY  (RecetteId, IngredientId, RecetteSeq),
   CONSTRAINT RECETTE_RECETTE_NOM_FK FOREIGN KEY (RecetteId)
      REFERENCES RECETTE_NOM (RecetteId),
   CONSTRAINT RECETTE_INGREDIENT_FK FOREIGN KEY (IngredientId)
      REFERENCES INGREDIENT (IngredientId) 
) ;

Code SQL :
1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE MAILLON (
   RecetteId              CHAR(04)             NOT NULL,
   IngredientId           CHAR(04)             NOT NULL,
   RecetteSeq             CHAR(04)             NOT NULL,
   RecettePrecedentSeq    CHAR(04)             NOT NULL,
   CONSTRAINT MAILLON_PK PRIMARY KEY  (RecetteId, IngredientId, RecetteSeq),
   CONSTRAINT MAILLON_AK UNIQUE (RecetteId, IngredientId, RecettePrecedentSeq),
   CONSTRAINT MAILLON_RECETTE_FK1 FOREIGN KEY (RecetteId, IngredientId, RecetteSeq)
      REFERENCES Recette (RecetteId, IngredientId, RecetteSeq),
   CONSTRAINT MAILLON_RECETTE_FK2 FOREIGN KEY (RecetteId, IngredientId, RecettePrecedentSeq)
      REFERENCES Recette (RecetteId, IngredientId, RecetteSeq)
) ;

Créons des recettes pour la paire <'r01', 'i01'> :

Code SQL :
1
2
3
4
5
INSERT INTO RECETTE VALUES ('r01', 'i01', '001', '2001-01-25', '2001-01-25', 100) ;
INSERT INTO RECETTE VALUES ('r01', 'i01', '002', '2001-01-25', '2001-01-26', 200) ;
INSERT INTO RECETTE VALUES ('r01', 'i01', '003', '2001-01-27', '2001-01-25', 250) ;
...
INSERT INTO RECETTE VALUES ('r01', 'i01', '150', '2001-01-30', '2001-01-31', 100) ;

Le maillon '002' a pour prédécesseur '001' qui lui-même n’est précédé par personne. Le maillon '003' a pour prédécesseur '002', etc. :

Code SQL :
1
2
3
4
INSERT INTO MAILLON VALUES ('r01', 'i01', '002', '001') ;
INSERT INTO MAILLON VALUES ('r01', 'i01', '003', '002') ;
...
INSERT INTO MAILLON VALUES ('r01', 'i01', '150', '149') ;

Créons une variante '151' de la recette <'r01', 'i01'>, qui se glissera le moment voulu entre '002' et '003' :

Code SQL :
INSERT INTO RECETTE VALUES ('r01', 'i01', '151', '2001-01-25', '2001-01-25', 150) ;

Faisons précéder '003' par '151' :

Code SQL :
1
2
3
4
5
UPDATE MAILLON 
       SET   RecettePrecedentSeq = '151'
       WHERE RecetteId = 'r01'
         AND IngredientId = 'i01'
         AND RecetteSeq = '003' ;

Et faisons glisser '151' entre 002' et '003' :

Code SQL :
INSERT INTO MAILLON VALUES ('r01', 'i01', '151', '002') ;

Cette méthode n’est pas gourmande en mise à jour, mais reste à savoir si elle vous convient, l’attribut OrdreInsertion passant le cas échéant à la trappe, ce qui serait gênant s’il a vraiment un rôle fonctionnel. A vous de voir et d’adapter.
__________________
_
Faites simple, mais pas plus simple ! (A. Einstein)
E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
=> La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

__________________

Bases de données relationnelles et normalisation : de la première à la sixième forme normale (Bonne lecture !)
fsmrel est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/04/2011, 09h07   #19
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
Whaou extraordinaire!

J'essayai justement de trouver un système similaire.

Je prend note, je relie et j'essaie de suite dans mon SGBD.

Mille merci.

Je vous tiens au courant
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/04/2011, 11h16   #20
Nouveau Membre du Club
 
Inscription : septembre 2008
Messages : 115
Détails du profil
Informations forums :
Inscription : septembre 2008
Messages : 115
Points : 28
Points : 28
J'aurais juste une petite remarque:

Code :
1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE MAILLON (
   RecetteId              CHAR(04)             NOT NULL,
   IngredientId           CHAR(04)             NOT NULL,
   RecetteSeq             CHAR(04)             NOT NULL,
   RecettePrecedentSeq    CHAR(04)             NOT NULL,
   CONSTRAINT MAILLON_PK PRIMARY KEY  (RecetteId, IngredientId, RecetteSeq),
   CONSTRAINT MAILLON_AK UNIQUE (RecetteId, IngredientId, RecettePrecedentSeq),
   CONSTRAINT MAILLON_RECETTE_FK1 FOREIGN KEY (RecetteId, IngredientId, RecetteSeq)
      REFERENCES Recette (RecetteId, IngredientId, RecetteSeq),
   CONSTRAINT MAILLON_RECETTE_FK2 FOREIGN KEY (RecetteId, IngredientId, RecettePrecedentSeq)
      REFERENCES Recette (RecetteId, IngredientId, RecetteSeq)
) ;
Ceci est parfaitement fonctionnel sir dans cet table j'insère des uplets contenant le même ingrédient, tel que vous le faite:

Code :
1
2
3
INSERT INTO RECETTE VALUES ('r01', 'i01', '001', '2001-01-25', '2001-01-25', 100) ;
INSERT INTO RECETTE VALUES ('r01', 'i01', '002', '2001-01-25', '2001-01-26', 200) ;
INSERT INTO RECETTE VALUES ('r01', 'i01', '003', '2001-01-27', '2001-01-25', 250) ;
Mai si je veux insérer ces lignes:
Code :
1
2
3
INSERT INTO RECETTE VALUES ('r01', 'i01', '001', '2001-01-25', '2001-01-25', 100) ;
INSERT INTO RECETTE VALUES ('r01', 'i11', '002', '2001-01-25', '2001-01-26', 200) ;
INSERT INTO RECETTE VALUES ('r01', 'i22', '003', '2001-01-27', '2001-01-25', 250) ;
Alors la clef étrangère "MAILLON_RECETTE_FK2" m'en empêchera.
Donc il ne faudrait pas faire cette déclaration de "MAILLON_RECETTE_FK2"?


Ensuite:

Je suis en train de faire une requêtes pour sélectionner la recette dans le bon ordre. Je m'aperçois que ce n'est pas un exercice facile. Pour l'instant je n'ai pas encore réussi. J'ai bien trouvé un système mais j'utilise beaucoup trop de requêtes pour y parvenir. Je m'y penche et vous transmet mes idées.

Merci
Vilukariok est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 06h24.


 
 
 
 
Partenaires

Hébergement Web