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

JFreesoft Discussion :

JMerise 0.5 (version test ) est disponible [JMerise]


Sujet :

JFreesoft

  1. #21
    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 JMerise 0.5, 3e lot de remarques
    Bonsoir rabDev,

    Je viens de lire les précisions que vous avez apportées dans le post #20.

    Citation Envoyé par rabDev Voir le message
    J'ai refait et accentué les vérifications sur les attributs des anciens modèles.
    Est-ce à dire qu’il faudra installer une nouvelle mouture de JMerise permettant d’éliminer automatiquement les anomalies provoquées par les MCD créés avec la version 0.4.0.1 ? Je pense ici à ceux qui ont créés des MCD disons plus ou moins volumineux...


    Citation Envoyé par rabDev Voir le message
    Comme vous le savez, on peut renommer les noms et les codes des attributs dans le MLD. Ensuite, il faut appuyer sur le bouton "Regénérer le script SQL" (voir l'image ci-dessous) pour générer le script correspondant aux modifications.
    Dans le MLD, j’ai effectivement modifié le nom de l’attribut utilisé pour la clé primaire de la table FACTURE : le script SQL a bien suivi (notamment en ce qui concerne la clé étrangère dans la table LIGNE_FACTURE).


    Citation Envoyé par rabDev Voir le message
    Dans les prochaines versions [...]
    Vous vous positionnez au plan MLD. Dans ce qui suit, j’insiste sur certains points concernant le plus souvent le MCD, mais il n’y a pas contradiction...


    Concernant les clés candidates (alternatives) :

    Je reviens sur le post #16 :

    Citation Envoyé par rabDev Voir le message
    Pour le moment, JMerise les déclare, dans le MCD, séparément mais dans le MLD, {cd_departement, libelle_dpt} forment une seule clé candidate.
    Conscient de ce problème, j'ai même commencé à développer cette partie mais elle ne sera prête que dans les prochaines versions.
    Dans ce qui suit, clé candidate est bien sûr à interpréter comme identifiant alternatif (candidat) si l’on se place au niveau du MCD merisien (cf. Ingénierie des systèmes d'information : Merise deuxième génération (4e édition, 2001), page 97).


    Attention ! Dans le MLD, la paire {cd_departement, libelle_dpt} n’est pas clé candidate de la table REF_DEPARTEMENT !

    En effet, pour mériter le qualificatif de « candidate », une telle clé doit être irréductible, c'est-à-dire que si on lui ôte un de ses éléments alors elle perd sa propriété d’unicité. Si on ôte l’attribut libelle_dpt, il reste le singleton {cd_departement} qui est, lui, clé candidate authentique. De même, si dans la paire {cd_departement, libelle_dpt}, on ôte l’attribut cd_departement, le singleton {libelle_dpt} est lui aussi clé candidate authentique. Ainsi doit-on disposer dans le MLD de deux clés candidates bien distinctes.

    Pour prendre un exemple encore plus parlant, passons à l’entité-type PERSONNE suivante :


    Outre le singleton {personneId} déclaré clé primaire, on a 4 autres clés candidates (dites encore alternatives), à savoir les singletons {personneMatricule}, {personneNoSecu}, {personneLogin}, {personneMail}. Au stade du code SQL, JMerise produit non pas 4 clés authentiquement candidates, mais une fausse « clé » (plus formellement une surclé réductible) :

    (personneMatricule, personneNoSecu, personneLogin, personneMail)

    Celui qui sait exactement ce qu’est une clé candidate s’empressera de corriger le code SQL, ou ne déclarera qu’une seule clé candidate à JMerise et, prudent, ajoutera à la main les 3 autres. Mais celui qui ne sait pas ce qu’est fondamentalement ce type de clé, n’y verra que du feu et, de façon inéluctable, au gré des inserts et updates affectant la table, les viols de la règle d’unicité se produiront.

    Moralité : tant que la déclaration d’authentiques clés candidates ne sera pas possible, n’en autorisez qu’une seule, verrouillez...

    Je fais aussi observer que dans un MCD bien construit, on ne rencontre pas d’identifiant « composé » (en tout cas c’est mon propre constat au bout de quelques décennies...), ce qui va dans le sens de mes remarques visant en fait à considérer comme inutile, voire dangereuse l’option que vous avez retenue de mettre les clés « dans le même sac ». Certes votre métamodèle est touché (une entité-type peut comporter de 0 à N identifiants alternatifs), mais n’en veuillez pas au vieux briscard, ce que j’écris c’est pour le bien de JMerise.


    Puisqu’on traite des contraintes. Au sujet des index :

    Les index font partie du niveau physique, au point que la norme SQL n’en fait aucunement mention, et pour cause, chaque SGBD concevant à sa façon ces objets du niveau fer à souder et orientés performance des applications. Contrairement à ce que suggère l’image ci-dessous, un index n’est pas une contrainte. Autrement dit, au niveau conceptuel (MCD) on doit rester muet au sujet des index, la séparation des niveaux doit être stricte, il y va de la pertinence et de la puissance de JMerise. Ainsi, dans l’image, la présence du terme« INDEX » est totalement déplacée, il y a mélange des genres ; par analogie c’est comme si on se sentait obligé de faire intervenir les vertus de la Compagnie du gaz pour parler de Léonard de Vinci, enfin c’est ce que je ressens...



    N.B. Des contraintes, il y en a de toutes sortes (contraintes de domaine, de colonne, de table, de base de données, contraintes fonctionnelles (CIF), etc.), aussi vu l’orientation prise dans l’image ci-dessus, il me paraît souhaitable de remplacer « contrainte » par quelque chose de plus précis, du genre « contrainte d’unicité », les autres contraintes faisant l’objet d’un autre onglet.
    (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.

  2. #22
    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 JMerise 0.5, 4e lot de remarques (généralisation/spécialisation, héritage)
    Bonsoir,


    MCD

    Partons de l’exemple suivant, qui parle de lui-même :


    Il n’y a pas de commentaire particulier à faire, si ce n’est que dans le cas de l’entité-type COLLABORATEUR, si l’on déclare l’attribut Matricule en tant qu’identifiant alternatif, il est hélas ! préférable de s’abstenir de déclarer l’attribut NIR lui aussi en tant qu’identifiant alternatif, pour les raisons exposées dans le 3e lot de remarques. En effet, au stade SQL, au lieu d’avoir deux clés alternatives {Matricule} et {NIR}, JMerise génère une clé alternative erronée {Matricule, NIR}. En attendant la mise à niveau de JMerise, c’est au stade SQL qu’on déclarera {NIR} comme clé alternative.

    Au plan ergonomique : Il faudrait montrer explicitement (à l’instar des autres AGL orientés Merise) comment mettre en oeuvre la généralisation/spécialisation (héritage), car l’ayant oublié, j’avoue avoir passé un bon moment à tourner en rond, pour enfin me rendre compte qu’il fallait tirer un lien direct (icône « Nouveau lien ») entre les entités-types concernées (par exemple PERSONNE et COLLABORATEUR), procédé certes simple, mais qui n’a rien d’intuitif. Une icône, ou à tout le moins une infobulle seraient les bienvenues pour guider celui qui modélise (et peut s’aider de vos vidéos, mais bon, là encore ça prend du temps pour trouver l’image correspondante...)


    MLD

    Faire observer que les contraintes (exclusion, totalité, partitionnement) ne sont pas reconduites et que leur mise en oeuvre est à la charge du concepteur. Cet avertissement est à faire figurer dans le script SQL (même chose pour les CIF, les contraintes d’inclusion et autres du même tonneau).


    Si on analyse le code SQL généré par JMerise, celui-ci paraît correct (à condition bien sûr de ne pas avoir plus d’un identifiant alternatif par entité-type dans le MCD).

    Code PostgreSQL : 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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
     
    ------------------------------------------------------------
    --        Script Postgre 
    ------------------------------------------------------------
    
    
    
    ------------------------------------------------------------
    -- Table: PERSONNE
    ------------------------------------------------------------
    CREATE TABLE public.PERSONNE(
    	PersonneId        SERIAL NOT NULL ,
    	AdresseCourriel   VARCHAR (64) NOT NULL  ,
    	CONSTRAINT PERSONNE_PK PRIMARY KEY (PersonneId) ,
    	CONSTRAINT PERSONNE_AK UNIQUE (AdresseCourriel)
    )WITHOUT OIDS;
    
    
    ------------------------------------------------------------
    -- Table: TIERS
    ------------------------------------------------------------
    CREATE TABLE public.TIERS(
    	PersonneId      INT  NOT NULL ,
    	NoSiret         NUMERIC (14,0)  NOT NULL ,
    	RaisonSociale   VARCHAR (14) NOT NULL  ,
    	CONSTRAINT TIERS_PK PRIMARY KEY (PersonneId) ,
    	CONSTRAINT TIERS_AK UNIQUE (NoSiret)
    
    	,CONSTRAINT TIERS_PERSONNE_FK FOREIGN KEY (PersonneId) REFERENCES public.PERSONNE(PersonneId)
    )WITHOUT OIDS;
    
    
    ------------------------------------------------------------
    -- Table: ADRESSE
    ------------------------------------------------------------
    CREATE TABLE public.ADRESSE(
    	PersonneId          INT  NOT NULL ,
    	AdresseId           INT2  NOT NULL ,
    	AdresseLigne2       VARCHAR (48) NOT NULL ,
    	AdresseLigne3       VARCHAR (48) NOT NULL ,
    	AdresseLigne4       VARCHAR (48) NOT NULL ,
    	AdresseCodePostal   VARCHAR (6) NOT NULL  ,
    	CONSTRAINT ADRESSE_PK PRIMARY KEY (PersonneId,AdresseId)
    
    	,CONSTRAINT ADRESSE_PERSONNE_FK FOREIGN KEY (PersonneId) REFERENCES public.PERSONNE(PersonneId)
    )WITHOUT OIDS;
    
    
    ------------------------------------------------------------
    -- Table: SERVICE
    ------------------------------------------------------------
    CREATE TABLE public.SERVICE(
    	ServiceId        SERIAL NOT NULL ,
    	ServiceLibelle   VARCHAR (48) NOT NULL  ,
    	CONSTRAINT SERVICE_PK PRIMARY KEY (ServiceId)
    )WITHOUT OIDS;
    
    
    ------------------------------------------------------------
    -- Table: COLLABORATEUR
    ------------------------------------------------------------
    CREATE TABLE public.COLLABORATEUR(
    	PersonneId     INT  NOT NULL ,
    	Matricule      CHAR (8)  NOT NULL ,
    	NIR            CHAR (13)  NOT NULL ,
    	Nom            VARCHAR (48) NOT NULL ,
    	Prenom         VARCHAR (48) NOT NULL ,
    	DateEmbauche   DATE  NOT NULL ,
    	Salaire        INT  NOT NULL ,
    	ServiceId      INT  NOT NULL  ,
    	CONSTRAINT COLLABORATEUR_PK PRIMARY KEY (PersonneId) ,
    	CONSTRAINT COLLABORATEUR_AK UNIQUE (Matricule)
    
    	,CONSTRAINT COLLABORATEUR_PERSONNE_FK FOREIGN KEY (PersonneId) REFERENCES public.PERSONNE(PersonneId)
    	,CONSTRAINT COLLABORATEUR_SERVICE0_FK FOREIGN KEY (ServiceId) REFERENCES public.SERVICE(ServiceId)
    )WITHOUT OIDS;
    
    
    ------------------------------------------------------------
    -- Table: EMPLOYE
    ------------------------------------------------------------
    CREATE TABLE public.EMPLOYE(
    	PersonneId   INT  NOT NULL ,
    	Profil       VARCHAR (14) NOT NULL  ,
    	CONSTRAINT EMPLOYE_PK PRIMARY KEY (PersonneId)
    
    	,CONSTRAINT EMPLOYE_COLLABORATEUR_FK FOREIGN KEY (PersonneId) REFERENCES public.COLLABORATEUR(PersonneId)
    )WITHOUT OIDS;
    
    
    ------------------------------------------------------------
    -- Table: DIRECTEUR
    ------------------------------------------------------------
    CREATE TABLE public.DIRECTEUR(
    	PersonneId   INT  NOT NULL ,
    	PrimeBilan   INT  NOT NULL  ,
    	CONSTRAINT DIRECTEUR_PK PRIMARY KEY (PersonneId)
    
    	,CONSTRAINT DIRECTEUR_COLLABORATEUR_FK FOREIGN KEY (PersonneId) REFERENCES public.COLLABORATEUR(PersonneId)
    )WITHOUT OIDS;

    Commentaires sur le script SQL :

    Pour les entités-types spécialisées, à savoir COLLABORATEUR, TIERS, EMPLOYE, DIRECTEUR, vous pouvez générer l’action de compensation ON DELETE CASCADE (clause FOREIGN KEY). En effet, si on supprime la personne Raoul, on le supprime forcément à tous les étages, par définition les spécialisations n’ont pas à s’opposer aux stimuli qui leur parviennent.

    TABLE ADRESSE :

    L’attribut AdresseId participe à l’identifiant relatif de l’entité-type ADRESSE, donc à la clé primaire de la table ADRESSE au stade SQL. Par définition, les valeurs prises par l’attribut AdresseId n’ont aucune signification, ces valeurs ne sont utilisées que pour distinguer les différentes adresses d’une personne. En conséquence, vous pouvez remplacer

    AdresseId INT2 NOT NULL

    par

    AdresseId SERIAL NOT NULL

    Ça nous évitera d’avoir à le faire, ou de mettre en oeuvre un trigger palliant l’absence de l’auto-incrémentation.

    Tant qu’à faire, il serait préférable de remplacer INT2 par SMALLINT, car ce ne sont pas tous les SGBD qui proposent INT2 qui n’est pas non plus proposé par la norme SQL.

    « WITHOUT OIDS » est la clause par défaut » et ne sert que si l’on utilise une ancienne version de PostgreSQL. Je cite la doc à propos du paramètre default_with_oids :


    Citation Envoyé par PostgreSQL 10.3
    default_with_oids (boolean)
    Contrôle si les commandes CREATE TABLE et CREATE TABLE AS incluent une colonne OID dans les tables nouvellement créées, lorsque ni WITH OIDS ni WITHOUT OIDS ne sont précisées. Ce paramètre détermine également si les OID sont inclus dans les tables créées par SELECT INTO. Ce paramètre est désactivé (off) par défaut ; avec PostgreSQL™ 8.0 et les versions précédentes, il était activé par défaut.

    L'utilisation d'OID dans les tables utilisateur est considérée comme obsolète. Il est donc préférable pour la plupart des installations de laisser ce paramètre désactivé. Les applications qui requièrent des OID pour une table particulière doivent préciser WITH OIDS lors de la création de la table. Cette variable peut être activée pour des raisons de compatibilité avec les anciennes applications qui ne suivent pas ce comportement.
    Citation Envoyé par PostgreSQL 10.3
    WITH OIDS, WITHOUT OIDS
    Ce sont les syntaxes obsolètes mais équivalentes, respectivement de WITH (OIDS) et WITH (OIDS=FALSE). Si vous souhaitez indiquer à la fois l'option OIDS et les paramètres de stockage, vous devez utiliser la syntaxe WITH ( ... ) ;.

    En passant : au cas où l’on a changé de SGBD, pour éviter de regénérer le script à partir du MCD, si l’on clique sur l’icône « SQL » il serait bien que la regénération du script soit faite en fonction du dernier SGBD choisi.


    Dans « Paramètres & Configuration » > « Configuration des paramètres » > « Contraintes MCD », paramètre « Importer tous les attributs (non clés primaires) de l’entité mère » : je ne sais pas quelle est l’option par défaut : case cochée ? décochée ? Pour les novices, il est évident que la case doit être décochée, sinon avec le MCD ci-dessus, le MLD devient :


    Où la redondance est pour le moins excessive... (avec un script SQL en conséquence, plus la démultiplication des identifiants alternatifs erronés, du genre {AdresseCourriel, Matricule}...)


    En passant :

    A l’image des autres AGL et logiciels prévoyez-vous la possibilité d’une option « tout sélectionner » (raccourci CTRL-A) permettant de sélectionner instantanément tous les objets d’une fenêtre ? (sans avoir à commencer par zoomer en tout petit...)

    L’entité-type : PERSONNE, ADRESSE, etc. sont des entités-types plutôt que des entités : une entité est une instance, une occurrence d’entité-type (Raoul, Fernand, etc.)
    (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. #23
    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 JMerise 0.5, 5e lot de remarques (contraintes d’inclusion)
    Bonsoir RabDev,

    Un détour du côté des contraintes d’inclusion.

    Partons du MCD que vous proposez avec la version 0.5 de JMerise :




    Et examinons la contrainte d’inclusion impliquant les associations « reservation » et « stocké dans ». Manifestement il manque une pointe de flèche, car en l’état, sans cet élément on ne sait pas quelle association est « incluse » dans l’autre.

    On doit donc avoir la représentation :


    Cette fois-ci, on sait comment interpréter l’inclusion sans ambiguïté.

    Par ailleurs, le « manuel de l’utilisateur de JMerise » devrait fournir toutes les indications permettant de modéliser cette contrainte. Je rappelle à ce sujet la recommandation faite par l’afcet :



    Exemple bref d’explication à usage du concepteur :

    Dans l’exemple proposé par JMerise, l’association réservation est la portée de la contrainte et l’association Stocké dans en est la cible. Le pivot de la contrainte est composé des seules entités-types Dépôt et Article, tandis que l’entité-type Client ne joue aucun rôle dans la contrainte, bien qu’elle participe à l’association réservation : les lignes en pointillés sont là pour préciser que le pivot est composé des seules entités-types Dépôt et Article.

    Passons au MLD.

    Puisqu’elle disparaît lors du passage du MCD au MLD, j’ai fait figurer manuellement la contrainte d’inclusion (avec sa pointe de flèche...) :




    Précisez qu’au stade MLD et SQL, l’expression de la contrainte est à la charge du concepteur...

    Dans le style de la norme SQL :

    CREATE ASSERTION RESERVER_INCLUSION_CHK
        CHECK (
               NOT EXISTS (SELECT  articleId, depotId    
                           FROM    INSERTED 
                           EXCEPT  
                           SELECT  articleId, depotId
                           FROM    STOCKER 
                          )
              ) ;
    
    Les SGBD du marché renâclant à proposer l’instruction CREATE ASSERTION, on est obligé d’en passer par des triggers (lesquels en théorie sont là pour produire et non pas pour contrôler, mais bref, passons...)

    Exemple avec SQL Server. Partons des déclarations suivantes :

    CREATE TABLE DEPOT 
    (
            depotId              SERIAL          NOT NULL
          , depotKode            VARCHAR(8)      NOT NULL
          , depotSigle           VARCHAR(12)     NOT NULL
          , depotNom             VARCHAR(64)     NOT NULL
          , depotAdresse         VARCHAR(127)    NOT NULL
          , depotTel             VARCHAR(24)     NOT NULL
        , CONSTRAINT DEPOT_PK PRIMARY KEY (depotId)
        , CONSTRAINT DEPOT_CODE_AK UNIQUE (depotKode)
    ) ;
    
    CREATE TABLE ARTICLE 
    (
            articleId            SERIAL          NOT NULL
          , articleCode          VARCHAR(24)     NOT NULL
          , articleLibelle       VARCHAR(128)    NOT NULL
          , articlePrixUnitaire  DECIMAL(10,2)   NOT NULL
        , CONSTRAINT ARTICLE_PK PRIMARY KEY (articleId)
        , CONSTRAINT ARTICLE_AK UNIQUE (articleCode)
    ) ;
    
    CREATE TABLE CLIENT 
    (
            clientId             SERIAL          NOT NULL
          , clientCode           VARCHAR(8)      NOT NULL
          , clientNom            VARCHAR(64)     NOT NULL
          , clientAdresse        VARCHAR(127)    NOT NULL
          , clientTel            VARCHAR(24)     NOT NULL
        , CONSTRAINT CLIENT_PK PRIMARY KEY (clientId)
        , CONSTRAINT CLIENT_CODE_AK UNIQUE (clientCode)
    ) ;
    
    CREATE TABLE STOCKER 
    (
            articleId            INT             NOT NULL
          , depotId              INT             NOT NULL
          , qteDisponible        DECIMAL(8,2)    NOT NULL
        , CONSTRAINT STOCKER_PK PRIMARY KEY (articleId, depotId)
        , CONSTRAINT STOCKER_DEPOT_FK FOREIGN KEY (depotId)
              REFERENCES DEPOT
        , CONSTRAINT STOCKER_ARTICLE_FK FOREIGN KEY (articleId)
              REFERENCES ARTICLE
    ) ;
    
    CREATE TABLE RESERVER 
    (
            clientId             INT             NOT NULL
          , articleId            INT             NOT NULL
          , depotId              INT             NOT NULL
          , qteReservee          DECIMAL(8,2)    NOT NULL
        , CONSTRAINT RESERVER_PK PRIMARY KEY (clientId, articleId, depotId)
        , CONSTRAINT RESERVER_CLIENT_FK FOREIGN KEY (clientId)
              REFERENCES CLIENT
        , CONSTRAINT RESERVER_ARTICLE_FK FOREIGN KEY (articleId)
              REFERENCES ARTICLE    
       , CONSTRAINT RESERVER_DEPOT_FK FOREIGN KEY (depotId)
              REFERENCES DEPOT
    ) ;
    

    Contrôle par trigger de la contrainte d’inclusion avec SQL Server :

    CREATE TRIGGER RESERVER_INCLUSION_TR ON RESERVER INSTEAD OF INSERT, UPDATE
    AS
        BEGIN
           IF EXISTS
                (SELECT  articleId, depotId    
                 FROM    INSERTED 
                 EXCEPT  
                 SELECT  articleId, depotId
                 FROM    STOCKER
                )
    
                BEGIN 
                    SELECT 'Viol de la contrainte d''inclusion !' AS Engueulade, * FROM INSERTED
    ----------------RAISERROR ('Viol de la contrainte d''inclusion ! !',16,1)  -- state = 16 pour bloquer  
                    RAISERROR ('Viol de la contrainte d''inclusion ! !',0,1)  -- state = 0 pour les tests 
                    RETURN
                END
    
            INSERT INTO RESERVER  
                 SELECT   clientId, articleId, depotId, qteReservee 
                 FROM    INSERTED
        END
    GO
    

    Exemple avec PostgreSQL :

    CREATE FUNCTION RESERVER_INCLUSION_FN()
        RETURNS TRIGGER AS
    $le_trigger$
        DECLARE Erreur     VARCHAR ;
        BEGIN
            IF EXISTS 
    	    (SELECT  (articleId, depotId)    
                 FROM    RESERVER 
                 EXCEPT  
                 SELECT  (articleId, depotId)
                 FROM    STOCKER) 					   
               THEN
                    Erreur = 'Viol de la contrainte d''inclusion, la paire <articleId = '|| NEW.articleId || ', depotId = ' || NEW.depotId || '> est absente du stock.' ;
                    RAISE EXCEPTION SQLSTATE '45001' USING MESSAGE = Erreur ;
            END IF ;
            RETURN NEW ;
        END ;
    
    $le_trigger$
      LANGUAGE plpgsql ;
    
    CREATE TRIGGER RESERVER_INCLUSION_TR AFTER INSERT OR UPDATE ON RESERVER
        FOR EACH ROW EXECUTE PROCEDURE RESERVER_INCLUSION_FN() ;
    

    Avec MySQL (incomplet algébriquement parlant) :

    DELIMITER GO
     
    CREATE TRIGGER RESERVER_INCLUSION_TR_INSERT AFTER INSERT ON RESERVER 
    FOR EACH ROW
        BEGIN
            SET @Depot = (SELECT depotNom FROM DEPOT WHERE depotId = NEW.depotId) ;
            SET @Article = (SELECT articleLibelle FROM ARTICLE WHERE articleId = NEW.articleId) ;
            IF EXISTS (SELECT * 
                       FROM   RESERVER AS x LEFT JOIN STOCKER AS y ON x.articleId = y.articleId AND x.depotId = y.depotId
                       WHERE  y.articleId IS NULL OR y.depotId  IS NULL)
                THEN
                  
                SET @erreur = CONCAT('Dépot = "', @Depot, '", Article = "', @Article, '" : demande de réservation incompatible avec le stock.') ;
                SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = @erreur ;
            END IF ;
        END 
    GO
     
    DELIMITER  ;
    

    MySQL ne permettant pas de déclarer le trigger à la fois pour INSERT et UPDATE, reste à créer le trigger pour UPDATE.

    Suite à toutes ces horreurs qu’il faut développer, sachons qu’il existe un moyen simple et radical de mettre en oeuvre la contrainte d’inclusion, et d’envoyer tous ces triggers à la poubelle.

    Reprenons la terminologie Afcet. Soit R l’association jouant le rôle de la portée (RESERVER dans l’exemple) et S l’association jouant le rôle de la cible (STOCKER dans l’exemple). Si lors du passage au MLD, R et S ont été transformées en tables, alors R est dotée des attributs (articleId, depotId dans l’exemple) permettant de définir une clé étrangère ({articleId, depotId} dans l’exemple) référençant la clé primaire (voire une clé alternative) de la table cible. Les clés étrangères jusque-là déclarées peuvent alors passer à la trappe.

    Prenons la déclaration de la table RESERVER :

     
    CREATE TABLE RESERVER 
    (
            clientId             INT             NOT NULL
          , articleId            INT             NOT NULL
          , depotId              INT             NOT NULL
          , qteReservee          DECIMAL(8,2)    NOT NULL
        , CONSTRAINT RESERVER_PK PRIMARY KEY (clientId, articleId, depotId)
        , CONSTRAINT RESERVER_CLIENT_FK FOREIGN KEY (clientId)
              REFERENCES CLIENT
        , CONSTRAINT RESERVER_ARTICLE_FK FOREIGN KEY (articleId)
              REFERENCES ARTICLE    
       , CONSTRAINT RESERVER_DEPOT_FK FOREIGN KEY (depotId)
              REFERENCES DEPOT) 
    ;
    
    Cette déclaration peut être remplacée par la suivante, indiscutablement bien plus robuste :

     
    CREATE TABLE RESERVER 
    (
            clientId             INT             NOT NULL
          , articleId            INT             NOT NULL
          , depotId              INT             NOT NULL
          , qteReservee          DECIMAL(8,2)    NOT NULL
        , CONSTRAINT RESERVER_PK PRIMARY KEY (clientId, articleId, depotId)
        , CONSTRAINT RESERVER_CLIENT_FK FOREIGN KEY (clientId)
              REFERENCES CLIENT
        , CONSTRAINT RESERVER_STOCKER_FK FOREIGN KEY (articleId, depotId)
              REFERENCES STOCKER
    ;
    
    Et bien sûr, les triggers dégagent de facto à destination de la poubelle, ce qui est le but de la manœuvre...

    MLD correspondant (autrement béton et sympathique) :




    Remarques diverses

    Dans votre exemple, il serait souhaitable d’éviter les identifiants significatifs, et d’en faire des identifiants alternatifs. Je rabâche, mais je ne me lasserai pas de citer Yves Tabourier...

    Qui écrit à la page 80 de son remarquable ouvrage (De l’autre côté de MERISE, Les Éditions d’organisation, 1986), ce qui constitue une règle d’or valant pour les identifiants des entités-types florissant dans les MCD merisiens (règle d’or trop souvent méconnue, hélas ! Et comme disent Goethe et Cie, « ceux qui ont oublié le passé sont condamnés à le revivre... ») :

    « ... la fonction d’une propriété est de décrire les objets (et les rencontres), alors que l’identifiant ne décrit rien. Son rôle fondamental est d’être sûr de distinguer deux jumeaux parfaits, malgré des descriptions identiques.
    L’expérience montre d’ailleurs que l’usage des “identifiants significatifs” (ou “codes significatifs”) a pu provoquer des dégâts tellement coûteux que la sagesse est d’éviter avec le plus grand soin de construire des identifiants décrivant les objets ou, pis encore, leurs liens avec d’autres objets... »

    Quand Tabourier parle des dégâts, je peux témoigner...

    Et il ne faudrait pas que des débutants en modélisation se disent que les identifiants significatifs sont une bonne chose, pour avoir vu dans l’interface de JMerise l’attribut SIGLE comme identifiant de l’entité-type DEPOT...


    MLD : il faudrait que l’on puisse présenter les attributs dans l’ordre qui nous convient (par exemple mettre avant les autres les attributs participant aux clés). Mais je suppose que vous êtes à la manœuvre à ce sujet.


    MLD : évitez de cocher systématiquement NULL pour les clés des tables issues d’associations :




    Code SQL. Type DECIMAL : n dont 2 devient parfois n dont -1...

     
    CREATE TABLE public.ARTICLE(
    	articleId             SERIAL NOT NULL ,
    	articleCode           VARCHAR (24) NOT NULL ,
    	articleLibelle        VARCHAR (127) NOT NULL ,
    	articlePrixUnitaire   DECIMAL (10,-1)  NOT NULL  ,
    ...
    CREATE TABLE public.STOCKER(
    	qteDisponible   DECIMAL (8,-1)  NOT NULL ,
    	articleId       INT  NOT NULL ,
    	depotId         INT  NOT NULL  ,
    ...
    CREATE TABLE public.RESERVER(
    	qteReservee   DECIMAL (8,2)  NOT NULL ,
    	articleId     INT  NOT NULL ,
    	depotId       INT  NOT NULL ,
    	clientId      INT  NOT NULL  ,
    
    Pour avoir quelque chose de correct, il faut actuellement éviter de saisir la partie décimale dans la fenêtre « Propriété de l’entité », mais le faire dans la fenêtre « Propriété de l’attribut »...

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

  4. #24
    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
    Pour continuer avec les contraintes d’inclusion.

    Cette histoire ne relève évidemment pas de la série des longs fleuves tranquilles !

    Prenons un autre exemple, celui des professeurs et de stages. Partons des règles de gestion des données suivantes :

    (RG01) Un professeur enseigne au moins une matière ;
    (RG02) Une matière est enseignée par 0 à plusieurs professeurs ;
    (RG03) Une matière fait l’objet de 0 à plusieurs stages ;
    (RG04) Un stage porte sur au moins et au plus une matière ;
    (RG05) Un professeur anime de 0 à plusieurs stages ;
    (RG06) Un stage est animé par au moins un professeur ;
    (RG07) Un professeur ne peut animer un stage que ci celui-ci porte sur une matière enseignée par ce professeur.

    Pour en revenir à la terminologie Afcet (cf. message précédent), la portée est l’association ANIMER. La cible ne peut être que l’association ENSEIGNER, car l’entité-type PROFESSEUR participant à ces deux associations, elle participe au pivot. L’association ANIMER n’étant pas directement branchée sur l’entité-type MATIERE, cette dernière ne participe pas au pivot.

    C’est du billard à 3 bandes...

    Le MCD peut être le suivant :



    Mais la contrainte dit seulement ceci :

    Pour animer des stages, un professeur doit enseigner.

    Ce qui n’est évidemment pas suffisant.

    Maintenant, dans ses explications, l’Afcet précise :

    « Si plusieurs relations R, R’, ... peuvent être "composées" sans ambiguïté, la contrainte peut avoir pour portée cette composition [...] »

    Je ne sais trop comment interpréter l’expression « sans ambiguïté », mais quoi qu’il en soit, il est certes possible de représenter par exemple ainsi la contrainte d’inclusion (où ANIMER et PORTER_SUR composent la portée) :



    Ou, moins indigeste :


    Ce qui s’interprète ainsi :

    Si un professeur anime un stage, et que ce stage porte sur une matière, alors ce professeur enseigne cette matière

    c'est-à-dire qu’on retrouve la règle de gestion (RG07).


    Si au stade MCD on arrive à s’en sortir, au stades MLD et SQL on est gros-jean comme devant, du fait de la composition des associations, ce qui fait qu’une assertion s’impose (ou à défaut un trigger)...


    CREATE ASSERTION PROF_ANIMER_STAGE_INCLUSION_CHK
        CHECK (
               NOT EXISTS (SELECT x.ProfId, y.MatiereId
                           FROM   INSERTED AS x JOIN STAGE AS y ON x.StageId = y.StageId 
                           EXCEPT  
                           SELECT ProfId, MatiereId
                           FROM   ENSEIGNER
                          )
              ) ;
    

    Trigger (SQL Server) :

    CREATE TRIGGER PROF_ANIMER_STAGE_INCLUSION_TR ON ANIMER INSTEAD OF INSERT, UPDATE
    AS
        BEGIN
            IF EXISTS 
                (SELECT x.ProfId, y.MatiereId
                 FROM   INSERTED AS x JOIN STAGE AS y ON x.StageId = y.StageId        
                 EXCEPT 
                 SELECT ProfId, MatiereId
                 FROM   ENSEIGNER)
    
                 BEGIN 
                    SELECT 'Viol de la contrainte d''inclusion !' AS Engueulade, * FROM INSERTED
    ----------------RAISERROR ('Viol de la contrainte d''inclusion ! !',16,1)  -- state = 16 pour bloquer  
                    RAISERROR ('Viol de la contrainte d''inclusion ! !',0,1)  -- state = 0 pour les tests 
                    RETURN
                 END
    
            INSERT INTO ANIMER  
               SELECT   StageId, ProfId 
               FROM     INSERTED
        END
    
    GO
    

    Un début de jeu d’essai :

     
    INSERT INTO MATIERE (MatiereId, MatiereNom) VALUES (1, 'm1') ;
    INSERT INTO MATIERE (MatiereId, MatiereNom) VALUES (2, 'm2') ;
    INSERT INTO MATIERE (MatiereId, MatiereNom) VALUES (3, 'm3') ;
    
    SELECT 'MATIERE', * FROM MATIERE ;
    
    INSERT INTO PROFESSEUR (ProfId, ProfNom) VALUES (1, 'p1') ;
    INSERT INTO PROFESSEUR (ProfId, ProfNom) VALUES (2, 'p2') ;
    INSERT INTO PROFESSEUR (ProfId, ProfNom) VALUES (3, 'p3') ;
    
    SELECT 'PROFESSEUR', * FROM PROFESSEUR ;
    
    INSERT INTO ENSEIGNER (MatiereId, ProfId) VALUES (1, 2) ;
    INSERT INTO ENSEIGNER (MatiereId, ProfId) VALUES (1, 3) ;
    INSERT INTO ENSEIGNER (MatiereId, ProfId) VALUES (2, 1) ;
    INSERT INTO ENSEIGNER (MatiereId, ProfId) VALUES (2, 2) ;
    INSERT INTO ENSEIGNER (MatiereId, ProfId) VALUES (3, 1) ;
    INSERT INTO ENSEIGNER (MatiereId, ProfId) VALUES (3, 3) ;
    
    SELECT 'ENSEIGNER', * FROM ENSEIGNER ;
    
    INSERT INTO STAGE (StageId, MatiereId, StageIntitule) VALUES (11, 1, 'm1 : s1') ;
    INSERT INTO STAGE (StageId, MatiereId, StageIntitule) VALUES (12, 1, 'm1 : s2') ;
    INSERT INTO STAGE (StageId, MatiereId, StageIntitule) VALUES (13, 1, 'm1 : s3') ;
    
    INSERT INTO STAGE (StageId, MatiereId, StageIntitule) VALUES (21, 2, 'm2 : s1') ;
    INSERT INTO STAGE (StageId, MatiereId, StageIntitule) VALUES (22, 2, 'm2 : s2') ;
    INSERT INTO STAGE (StageId, MatiereId, StageIntitule) VALUES (23, 2, 'm2 : s3') ;
    
    SELECT 'STAGE', * FROM STAGE ;
    
    INSERT INTO ANIMER (StageId, ProfId) VALUES (11, 2) ;  -- correct
    INSERT INTO ANIMER (StageId, ProfId) VALUES (11, 1) ;  -- incorrect
    
    SELECT 'ANIMER', * FROM ANIMER ;
    
    Bref, avant d’envisager d’automatiser les simplifications, un travail de classification des contraintes d’inclusion s’impose (contraintes directes, indirectes et surprises diverses...)

    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.

  5. #25
    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 RabDev,


    Au sujet de l’entité-type DATE et autres pseudo entités-types

    L’entité-type DATE... Il s’agit d’un sujet sur lequel je reviens régulièrement...


    Partons de l’exemple suivant, avec les règles de gestion des données :

    A une date donnée, un fournisseur donné peut fournir plusieurs produits,

    A une date donnée, un produit donné peut être fourni par plusieurs fournisseurs,

    Un fournisseur donné peut fournir un produit donné à des dates différentes.


    MCD correspondant


    Dans ce MCD, DATE est une pseudo entité-type, sa présence est rendue nécessaire parce que la date doit participer à l’identification de l’association LIVRER. Sa mise en oeuvre n’est ici qu’un artifice incontournable pour régler le problème de l’identification.

    MLD généré


    Code SQL généré

    #------------------------------------------------------------
    # Table: DATE
    #------------------------------------------------------------
    
    CREATE TABLE DATE(
            DateLivraison Date NOT NULL
    	,CONSTRAINT DATE_PK PRIMARY KEY (DateLivraison)
    )ENGINE=InnoDB;
    
    
    #------------------------------------------------------------
    # Table: FOURNISSEUR
    #------------------------------------------------------------
    
    CREATE TABLE FOURNISSEUR(
            FourId  Int NOT NULL ,
            FourNom Varchar (50) NOT NULL
    	,CONSTRAINT FOURNISSEUR_PK PRIMARY KEY (FourId)
    )ENGINE=InnoDB;
    
    
    #------------------------------------------------------------
    # Table: PRODUIT
    #------------------------------------------------------------
    
    CREATE TABLE PRODUIT(
            ProdId  Int NOT NULL ,
            ProdNom Varchar (50) NOT NULL
    	,CONSTRAINT PRODUIT_PK PRIMARY KEY (ProdId)
    )ENGINE=InnoDB;
    
    #------------------------------------------------------------
    # Table: LIVRER
    #------------------------------------------------------------
    
    CREATE TABLE LIVRER(
            Quantite      Int NOT NULL ,
            DateLivraison Date NOT NULL ,
            ProdId        Int NOT NULL ,
            FourId        Int NOT NULL
    	,CONSTRAINT LIVRER_PK PRIMARY KEY (DateLivraison,ProdId,FourId)
    
    	,CONSTRAINT LIVRER_DATE_FK FOREIGN KEY (DateLivraison) REFERENCES DATE(DateLivraison)
    	,CONSTRAINT LIVRER_PRODUIT0_FK FOREIGN KEY (ProdId) REFERENCES PRODUIT(ProdId)
    	,CONSTRAINT LIVRER_FOURNISSEUR1_FK FOREIGN KEY (FourId) REFERENCES FOURNISSEUR(FourId)
    )ENGINE=InnoDB;
    
    
    Quelques observations

    Pouvoir ne pas générer telle ou telle table, la table DATE en l’occurrence, sinon on doit modifier manuellement le script SQL : suppression de l’instruction CREATE TABLE DATE, suppression de toutes les contraintes référentielles visant cette table (contrainte LIVRER_DATE_FK de la table LIVRER dans cet exemple). Voir ici un exemple de demande de non génération.

    A cette occasion, dans le MLD, pouvoir présenter les attributs dans l’ordre qui convient au concepteur et, comme vous l’aviez annoncé ici, pouvoir réordonner les attributs dans les clés (crucial !)

    Par voie de conséquence, pouvoir regénérer le script SQL une fois le MLD modifié.
    (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.

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. [Généralités] Site officiel + Aide en ligne + Cours & formations WinDev + version test + Migration + TP
    Par Emmanuel Lecoester dans le forum WinDev
    Réponses: 0
    Dernier message: 07/03/2010, 13h59
  2. Réponses: 5
    Dernier message: 13/03/2008, 18h12
  3. Réponses: 16
    Dernier message: 27/02/2008, 09h12
  4. Test est-ce qu'une table existe
    Par jyvaut75 dans le forum VBA Access
    Réponses: 2
    Dernier message: 06/08/2007, 21h08

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