IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Modélisation Discussion :

Base de données recette + historique


Sujet :

Modélisation

  1. #1
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    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 ?

  2. #2
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut
    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 !
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, 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
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  3. #3
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    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)?

  4. #4
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut
    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 !
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, 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
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  5. #5
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    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?

  6. #6
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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...
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, 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
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  7. #7
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    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.

    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

  8. #8
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 ?
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, 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
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  9. #9
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    Bonjour,

    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

    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

    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.

  10. #10
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    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".

  11. #11
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut
    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...
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, 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
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  12. #12
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    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:

    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

  13. #13
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut
    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.
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, 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
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  14. #14
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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?

  15. #15
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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...
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, 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
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  16. #16
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    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

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

    Si c’est plutôt la quantité qui vous intéresse :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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...

  17. #17
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    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?

  18. #18
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    INSERT INTO RECETTE VALUES ('r01', 'i01', '151', '2001-01-25', '2001-01-25', 150) ;

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

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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.
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, 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
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  19. #19
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    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

  20. #20
    Membre du Club
    Inscrit en
    Septembre 2008
    Messages
    115
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 115
    Points : 60
    Points
    60
    Par défaut
    J'aurais juste une petite remarque:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Access Base de donnée : Recette
    Par ita7169 dans le forum IHM
    Réponses: 4
    Dernier message: 06/05/2014, 11h20
  2. Vijeo designer et base de données recettes
    Par quentin22breizh dans le forum Automation
    Réponses: 0
    Dernier message: 18/12/2012, 14h00
  3. [MySQL] Gestionnaire historique de la base de données
    Par Delors dans le forum PHP & Base de données
    Réponses: 3
    Dernier message: 02/09/2011, 19h12
  4. Cours, tutoriels, comparatif, et historique Bases de données
    Par Idelways dans le forum Décisions SGBD
    Réponses: 0
    Dernier message: 21/03/2008, 19h58
  5. Historique Accès à une base de données...
    Par JeremieT dans le forum Access
    Réponses: 12
    Dernier message: 09/03/2006, 14h23

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo