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

Looping Discussion :

Au sujet des contraintes d'inclusion


Sujet :

Looping

  1. #1
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut Au sujet des contraintes d'inclusion
    Histoire de faire le point, je déterre un sujet que nous avions abordé il y a 3 ans et qui concerne les contraintes d’inclusion.

    Looping permet de modéliser ces contraintes dans les MCD. J’avais pris comme exemple la contrainte proposée par le groupe 135 de l’afcet, contrainte selon laquelle un enseignant ne peut enseigner une matière à une classe que s’il sait enseigner cette matière :
    Nom : Inclusion.jpg
Affichages : 784
Taille : 172,2 Ko

    Version Looping :

    Nom : inclusion(looping)fsm.png
Affichages : 797
Taille : 19,6 Ko


    Las ! Le problème demeure quant à la production du code SQL : la contrainte est ignorée.

    Comme dit Paprick, « la traduction correcte et automatique de tous les cas de contraintes d'inclusion, et autres contraintes inter-associations, paraît complexe... »

    Certes. Néanmoins, dans le cas qui nous concerne, lors de la génération SQL, pour la table ENSEIGNE, il suffit de ne pas faire participer les « pivots » ENSEIGNANT et MATIERE, car les attributs enseignantId et matiereId sont hérités de la table SAIT_ENSEIGNER. Plus généralement, oser court-circuiter les pivots.

    Merci Paprick de me proposer des contre-exemples.

    J’ai évidemment dans ma besace une solution qui fera hurler les merisiens purs et durs, mais qui fournit le code SQL sans devoir en passer par l’outil Règle :

    Nom : inclusion(looping)fsmIdrel.png
Affichages : 778
Taille : 8,9 Ko

    Code SQL proposé par Looping :

    CREATE TABLE ENSEIGNANT(
       enseignantId INT,
       CONSTRAINT ENSEIGNANT_PK PRIMARY KEY(enseignantId)
    );
    
    CREATE TABLE MATIERE(
       matiereId INT,
       CONSTRAINT MATIERE_PK PRIMARY KEY(matiereId)
    );
    
    CREATE TABLE CLASSE(
       classeId INT,
       CONSTRAINT CLASSE_PK PRIMARY KEY(classeId)
    );
    
    CREATE TABLE SAIT_ENSEIGNER(
       enseignantId INT,
       matiereId INT,
       CONSTRAINT SAIT_ENSEIGNER_PK PRIMARY KEY(enseignantId, matiereId),
       CONSTRAINT SAIT_ENSEIGNER_ENSEIGNANT_FK FOREIGN KEY(enseignantId) REFERENCES ENSEIGNANT(enseignantId),
       CONSTRAINT SAIT_ENSEIGNER_MATIERE_FK FOREIGN KEY(matiereId) REFERENCES MATIERE(matiereId)
    );
    
    CREATE TABLE ENSEIGNER(
       enseignantId INT,
       matiereId INT,
       classeId INT,
       CONSTRAINT ENSEIGNER_PK PRIMARY KEY(enseignantId, matiereId, classeId),
       CONSTRAINT ENSEIGNER_SAIT_ENSEIGNER_FK FOREIGN KEY(enseignantId, matiereId) REFERENCES SAIT_ENSEIGNER(enseignantId, matiereId),
       CONSTRAINT ENSEIGNER_CLASSE_FK FOREIGN KEY(classeId) REFERENCES CLASSE(classeId)
    );
    Affaire à suivre...

  2. #2
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 397
    Points : 39 932
    Points
    39 932
    Billets dans le blog
    9
    Par défaut
    Voici un modèle assez peu intuitif à cause des acteurs fictifs qui n'en facilitent pas la compréhension ni le parallèle avec les règles de gestion

    Avoir à coder un peu de DDL pour les quelques contraintes me semble préférable à cette construction, dont résulte un grand nombre de tables
    Il conviendra de consolider tout ça dans des vues pour que ce soit un peu plus accessible au béotien

    Cela étant dit François, je suis heureux de voir que tu as retrouvé toute ta sagacité !

  3. #3
    Membre émérite
    Avatar de Paprick
    Homme Profil pro
    Professeur des Universités
    Inscrit en
    Juin 2019
    Messages
    713
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Professeur des Universités
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2019
    Messages : 713
    Points : 2 889
    Points
    2 889
    Par défaut
    Bonsoir François,

    Nous revoilà sur un débat que l'on connait bien !
    Tout d'abord, ta proposition de modèle pourrait avoir une présentation un peu simple et lisible ; en effet il est inutile de décomposer l'association "Enseigner" :
    Nom : MCD fsmrel 2.jpg
Affichages : 739
Taille : 23,7 Ko
    Concernant le codage de la contrainte d'inclusion, il est vrai que, dans le cas spécifique que tu proposes, le codage semble naturel et systématique.
    Comme tu le sais, ce qui me gêne le plus, c'est proposer la prise en compte de ce cas, et ne rien faire pour les autres contraintes inter-associatives : l'utilisateur pourrait ne pas s'y retrouver...
    Une solution qui pourrait être cohérente serait que Looping propose automatiquement, dans la définition de la contrainte, le code SQL correspondant à cette contrainte d'inclusion avec un ALTER TABLE, laissant ensuite l'utlisateur libre d'en faire ce qu'il veut.
    Qu'en penses-tu ? Peux-tu me formaliser ce code en fonction des configurations conceptuelles (pivots, ...) ?

  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 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Merci Capitaine de tes encouragements. La reprise n’est pas aisée, mais je me remets doucement et progressivement à l’entraînement...

    Citation Envoyé par escartefigue
    Voici un modèle assez peu intuitif à cause des acteurs fictifs qui n'en facilitent pas la compréhension.
    Bien entendu, et j'avais prévenu comme quoi la sémantique était malmenée :

    Citation Envoyé par fsmrel
    J’ai évidemment dans ma besace une solution qui fera hurler les merisiens purs et durs, mais qui fournit le code SQL sans devoir en passer par l’outil Règle.
    Considère cela seulement comme un exercice pour quelqu’un qui sait plutôt produire des MLD en cinquième forme normale sans pour autant connaître SQL.

    Voilà un autre exercice, plus compliqué, dans lequel les fameux pivots n'interviennent pas. C'est la reprise de l’exemple b) fourni dans le document afcet de 1990, page 46 :

    Nom : inclusion_afcet-figure b.png
Affichages : 732
Taille : 13,2 Ko

    Je sens qu’il y a dans l’air de la règle pure et dure, voire plus que ça...

  5. #5
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Salve Paprick !

    Citation Envoyé par Paprick Voir le message
    Comme tu le sais, ce qui me gêne le plus, c'est proposer la prise en compte de ce cas, et ne rien faire pour les autres contraintes inter-associatives : l'utilisateur pourrait ne pas s'y retrouver...
    C’est vrai. Il y aurait là un côté frustrant : pourquoi cette contrainte et pas les autres...


    Citation Envoyé par Paprick Voir le message
    Une solution qui pourrait être cohérente serait que Looping propose automatiquement, dans la définition de la contrainte, le code SQL correspondant à cette contrainte d'inclusion avec un ALTER TABLE, laissant ensuite l'utilisateur libre d'en faire ce qu'il veut.
    Ça me fait revenir en mémoire les triggers de Nanci, aïe ! aïe !...


    Citation Envoyé par Paprick Voir le message
    Peux-tu me formaliser ce code en fonction des configurations conceptuelles (pivots, ...) ?
    Après un an sans modéliser ni coder, je me sens rouillé et essaie donc de reprendre l’entraînement et remonter la pente. Je regarderai tout ça, calmement, mais la barre est bien haute, on grimpe vers le niveau méta, peut-être est-ce trop ? En effet, quand je vois comment des pointures comme Dominique Nanci (RIP) et ses collègues se sont pris les pieds dans le tapis avec leurs triggers (cf.
    Ingénierie des systèmes d’information] : Merise deuxième génération, 4°édition (2001)
     , je ne promets rien, d’autant plus que je suis encore convalescent, et donc pas en pleine possession de mes moyens...

  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 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Ave !

    Paprick, j’ai regardé ce qu’on pouvait faire avec la contrainte d’inclusion selon laquelle Raoul ne peut conduire un char Patton que s’il a le permis correspondant.

    Partons du MCD :

    Nom : inclusion(looping)fsm2.png
Affichages : 1245
Taille : 14,7 Ko

    Et voyons voir la traduction en SQL, sans tenir compte de la contrainte :

    CREATE TABLE PERSONNE 
    (
        PersonneId INT NOT NULL
      , PersonneNom VARCHAR(32) NOT NULL
      , PRIMARY KEY (PersonneId)
    );
    
    CREATE TABLE CATEGORIE 
    (
        CategorieId INT NOT NULL
      , CategorieNom VARCHAR(32) NOT NULL
      , PRIMARY KEY (CategorieId)
      , UNIQUE (CategorieNom)
    );
    
    CREATE TABLE PSN_CAT 
    (
        PersonneId INT NOT NULL
      , CategorieId INT NOT NULL
      , PermisDate DATE NOT NULL
      , PRIMARY KEY (PersonneId, CategorieId)
      , CONSTRAINT PSN_CAT_PSN_FK
          FOREIGN KEY (PersonneId)
          REFERENCES PERSONNE (PersonneId)
          ON DELETE CASCADE
      , CONSTRAINT PSN_CAT_CAT_FK
          FOREIGN KEY (CategorieId)
          REFERENCES CATEGORIE (CategorieId)
          ON DELETE NO ACTION
    );
    
    CREATE TABLE VEHICULE 
    (
      VehiculeId INT NOT NULL,
      CategorieId INT NOT NULL,
      VehiculeImmat VARCHAR(12) NOT NULL,
      PRIMARY KEY (VehiculeId),
      UNIQUE (VehiculeImmat),
      UNIQUE (VehiculeId, CategorieId),   -- pour inclusion
      CONSTRAINT VEH_CAT_FK
        FOREIGN KEY (CategorieId)
        REFERENCES CATEGORIE (CategorieId)
        ON DELETE NO ACTION
    );
    
    CREATE TABLE CONDUIRE 
    (
        PersonneId INT NOT NULL
      , VehiculeId INT NOT NULL
      , PRIMARY KEY (PersonneId, VehiculeId)
      , CONSTRAINT CON_PSN_FK
          FOREIGN KEY (PersonneId)
          REFERENCES PERSONNE (PersonneId)
          ON DELETE CASCADE
      , CONSTRAINT CON_VEH_FK
          FOREIGN KEY (VehiculeId)
          REFERENCES VEHICULE (VehiculeId)
          ON DELETE CASCADE 
    );
    
    Créons quelques lignes dans les tables :

    INSERT INTO PERSONNE VALUES (1, 'Fernand') ;
    INSERT INTO PERSONNE VALUES (2, 'Raoul') ;
    INSERT INTO PERSONNE VALUES (3, 'Paul') ;
    
    SELECT * FROM PERSONNE ;
    PersonneId    PersonneNom
    1             Fernand
    2             Raoul
    3             Paul
    INSERT INTO CATEGORIE VALUES (1, 'catégorie VL') ;
    INSERT INTO CATEGORIE VALUES (2, 'catégorie PL') ;
    INSERT INTO CATEGORIE VALUES (3, 'char Patton') ;
    
    SELECT * FROM CATEGORIE ;
    CategorieId   CategorieNom
    1             catégorie VL
    2             catégorie PL
    3             char Patton
    INSERT INTO PSN_CAT VALUES (1, 1, '1950-01-04') ;
    INSERT INTO PSN_CAT VALUES (1, 2, '1960-01-01') ;
    INSERT INTO PSN_CAT VALUES (1, 3, '1952-12-21') ;
    INSERT INTO PSN_CAT VALUES (2, 1, '1950-01-05') ;
    INSERT INTO PSN_CAT VALUES (2, 2, '1950-01-05') ; 
    
    SELECT * FROM PSN_CAT ;
    PersonneId   CategorieId   PermisDate
    1            1             1950-01-04
    1            2             1960-01-01
    1            3             1952-12-21
    2            1             1950-01-05
    2            2             1950-01-05
    INSERT INTO VEHICULE VALUES (1, 1, '1A75') ;
    INSERT INTO VEHICULE VALUES (2, 1, '1Z44') ;
    INSERT INTO VEHICULE VALUES (3, 2, '1H91') ;
    INSERT INTO VEHICULE VALUES (4, 3, '9G38') ;
    
    SELECT * FROM VEHICULE ; 
    VehiculeId   CategorieId   VehiculeImmat
    1            1             1A75
    2            1             1Z44
    3            2             1H91
    4            3             9G38
    INSERT INTO CONDUIRE VALUES (1, 1) ;
    INSERT INTO CONDUIRE VALUES (1, 2) ;
    INSERT INTO CONDUIRE VALUES (1, 4) ;
    INSERT INTO CONDUIRE VALUES (2, 3) ;
    
    SELECT * FROM CONDUIRE ; 
    PersonneId   VehiculeId
    1            1
    1            2
    1            4
    2            3 
    A ce stade, rien n’interdit que Raoul conduise un char Patton, alors qu’il n’a pas le permis ad-hoc. Pour empêcher cela, on crée l’assertion qui va bien, quelque chose comme cela  :
    CREATE ASSERTION INCL_ASSERT 
    CHECK
    (NOT EXISTS 
      (
        SELECT  *
        FROM 
          (
           SELECT e.personneid, d.categorieid
           FROM VEHICULE AS d INNER JOIN CONDUIRE AS e ON d.vehiculeid = e.vehiculeid
          ) AS s
        WHERE NOT EXISTS 
          (
           SELECT * 
           FROM 
             (
              SELECT e.personneid, d.categorieid
              FROM VEHICULE as d INNER JOIN CONDUIRE AS e ON d.vehiculeid = e.vehiculeid
               INNER JOIN PSN_CAT c ON c.categorieid = d.categorieid 
                  AND e.personneid = c.personneid
            ) AS a
           WHERE s.personneid = a.personneid 
               AND s.categorieid = a.categorieid
          )
      );
    Peu importe la lourdeur du code de cette assertion, je fais simplement observer que les 3 tables impliquées sont celles qui correspondent aux 3 associations du MCD.

    Maintenant, je ne sache pas que les éditeurs de SGBD aient envie de proposer l’instruction CREATE ASSERTION, laquelle fait quand même partie de la norme SQL depuis une trentaine d’années (SQL/92 me semble-t-il). Cette instruction est déclarée gourmande par ses détracteurs, qui attendent sans doute l’utilisation des ordinateurs quantiques pour changer d’avis....

    Alors, que faire ?

    Intégrer la contrainte CHECK dans la table CONDUIRE ? Niet ! Les SGBD déjouent la manoeuvre...
    Par exemple, avec SQL Server :

    Citation Envoyé par SQL Server
    Msg 1046, Niveau 15, État 1
    Les sous-requêtes ne sont pas autorisées dans ce contexte. Seules sont permises les expressions scalaires.
    C’est-à-dire que seuls les contrôles les plus basiques sont légaux.

    En passer par des triggers ? On se met à programmer avec les BEGIN/AFTER, for each row, etc.

    Avant d’en arriver là, on peut préférer mettre en oeuvre une table reprenant la contrainte d’inclusion :

    CREATE TABLE INCLUSION 
    (
        PersonneId INT NOT NULL
      , VehiculeId INT NOT NULL
      , CategorieId INT NOT NULL
      , PRIMARY KEY (PersonneId, VehiculeId)
      , CONSTRAINT PSN_CAT_FK
          FOREIGN KEY (PersonneId, CategorieId)
          REFERENCES PSN_CAT (PersonneId, CategorieId)
          ON DELETE NO ACTION
      , CONSTRAINT VEH_FK
          FOREIGN KEY (VehiculeId, CategorieId)
          REFERENCES VEHICULE (VehiculeId, CategorieId)
          ON DELETE CASCADE      
    );
    Avec l'ajout d’une clé étrangère dans la table CONDUIRE (du coup la clé étrangère CON_VEH_FK dans la table CONDUIRE peut être supprimée) :
    ALTER TABLE CONDUIRE
    ADD CONSTRAINT INCLUSION_FK 
      FOREIGN KEY (PersonneId, VehiculeId)
      REFERENCES INCLUSION  (PersonneId, VehiculeId)
    ;
    Quelques inserts de contrôle dans la table INCLUSION :
    INSERT INTO INCLUSION VALUES (1, 1, 1) ;  -- psn, veh, cat
    INSERT INTO INCLUSION VALUES (1, 2, 1) ;  -- psn, veh, cat
    INSERT INTO INCLUSION VALUES (1, 4, 3) ;  -- psn, veh, cat
    INSERT INTO INCLUSION VALUES (2, 3, 2) ;  -- psn, veh, cat 
    Et tentative d'infraction dans la table CONDUIRE (Raoul et son char Patton) :
    INSERT INTO CONDUIRE  VALUES (2, 4)
    =>
    Citation Envoyé par SQL Server
    Msg 547, Niveau 16, État 0
    L'instruction INSERT est en conflit avec la contrainte FOREIGN KEY "INCLUSION_FK".
    Mission accomplie.

    Bien entendu la table INCLUSION ne respecte pas la deuxième forme normale puisque l’attribut CategorieId dépend de l’attribut VehiculeId, donc d’une partie de la clé primaire, d’où redondance, mais on a quand même mis ceinture, bretelles et épingle à nourrice.

    Vos suggestions ?

  7. #7
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Hello !

    Capitaine, j’en reviens aux observations que tu fis dans le post #2

    Citation Envoyé par Escartefigue
    Voici un modèle assez peu intuitif à cause des acteurs fictifs qui n'en facilitent pas la compréhension ni le parallèle avec les règles de gestion.
    Tu as bien sûr raison, Capitaine, et les observations de Maître Paprick corroborent bien sûr ce que tu as écrit.

    J’ai réfléchi à cela. J’en conclus qu’il ne faut pas altérer la qualité d’un MCD, il faut conserver la sémantique dont il est porteur, sa simplicité, voire son esthétique, son élégance et toutes ces sortes de choses.

    Mais alors, comment produire le code SQL attendu ?

    Citation Envoyé par Escartefigue
    Avoir à coder un peu de DDL pour les quelques contraintes me semble préférable à cette construction, dont résulte un grand nombre de tables.
    Au vu du code SQL figurant dans le post #6, la manip n’est pas si simple : création d’une table INCLUSION pour traduire la contrainte d’inclusion, avec les nouvelles clés étrangères qui vont bien, sans oublier de supprimer celles qui sont devenues inutiles, donc pénalisantes.

    Pour ne pas en arriver là, à mon sens, la production du code SQL ne devrait pas nécessairement être effectuée directement à partir du MCD, mais au besoin, à partir du MLD ! Autrement dit, je dois avoir la main sur le MLD, qui d’entrée devrait faire figurer chaque colonne de chaque table, chaque clé (primaire, alternative). Partant de là, je dois pouvoir modifier ce MLD : mise en oeuvre de la table INCLUSION, révision des clés en conséquence.

    Désolé de choquer de la sorte, mais j’essaie de faire avancer le schmilblick...

    Je sens que ça va réagir !

  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 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Bonne année à tous !

    Paprick, j'ai fait la peau à la table INCLUSION
    J'ai un peu réfléchi et bien simplifié les choses, je t'en reparlerai dans la soirée, après un bon petit foie gras


    Bonne journée à toi, le lève tôt...

  9. #9
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    En passant, je rappelle le MCD original de la partie dont j’ai traité dans le post #6 :


    Nom : inclusion(looping)fsm2.png
Affichages : 1245
Taille : 14,7 Ko


    Le but de la manœuvre est de produire un MLD (et le code SQL) où l’on garantit la contrainte d’inclusion présente dans ce MCD. Je suis parti du principe suivant :

    De la contrainte d’inclusion je fais carrément une table, que je nomme par exemple INCLUSION.  

    Je rappelle le code SQL pour la partie qui concerne cette table (cf. le post #6), c’est-à-dire le code des tables avec lesquelles INCLUSION est en relation, mais ici, je ne fais pas figurer les clés étrangères :


    CREATE TABLE PSN_CAT 
    (
        PersonneId INT NOT NULL
      , CategorieId INT NOT NULL
      , PermisDate DATE NOT NULL
      , PRIMARY KEY (PersonneId, CategorieId)
    );
    
    CREATE TABLE VEHICULE 
    (
        VehiculeId INT NOT NULL
      , CategorieId INT NOT NULL
      , VehiculeImmat VARCHAR(12) NOT NULL
      , PRIMARY KEY (VehiculeId)
    );
    
    CREATE TABLE CONDUIRE 
    (
        PersonneId INT NOT NULL
      , VehiculeId INT NOT NULL
      , PRIMARY KEY (PersonneId, VehiculeId)
    );
    CREATE TABLE INCLUSION 
    (
        PersonneId INT NOT NULL
      , VehiculeId INT NOT NULL
      , CategorieId INT NOT NULL
      , PRIMARY KEY (PersonneId, VehiculeId)
    ); 
    Voici un pseudo-MLD dans lequel INCLUSION est en relation avec les 3 tables inférées de la contrainte d’inclusion, PSN_CAT (cible de la contrainte), VEHICULE et CONDUIRE (sources de la contrainte).


    Nom : inclusion(looping)avecInclusionMLDsansfleches_conduirebi-colonnes.png
Affichages : 661
Taille : 14,6 Ko

    La table INCLUSION est dotée des trois attributs PersonneId, CategorieId, VehiculeId, puisque ce sont les attributs identifiants des entités-types (classes d’entités) impliquées dans la contrainte d’inclusion.

    La clé de la table INCLUSION est composée du triplet {PersonneId, CategorieId, VehiculeId} car pour une personne et une catégorie on peut avoir plus d’un véhicule.
    Cela dit, on sait qu’il existe la dépendance fonctionnelle VehiculeId -> CategorieId, ce qui veut dire que la table n’est pas en troisième forme normale, mais ça n’est pas rédhibitoire.

    Il s’agit maintenant d’orienter les liens sans flèches.

    Lien entre INCLUSION et PSN_CAT : PSN_CAT (cible) ne peut pas faire référence à INCLUSION, car VehiculeId n’est pas un attribut de PSN_CAT. En revanche, les attributs PersonneId et CategorieId composent la clé primaire de la table PSN_CAT et peuvent composer une clé étrangère dans INCLUSION pour référencer PSN_CAT.

    Même principe concernant le lien entre INCLUSION et VEHICULE, les attributs PersonneId et CategorieId n’appartenant pas à la table VEHICULE, le lien ne peut être orienté que dans le sens INCLUSION vers VEHICULE.

    Reste le problème de l’orientation du lien entre INCLUSION et CONDUIRE. Là encore, le lien est nécessairement orienté dans le sens INCLUSION vers CONDUIRE.
    Mais si on oriente ainsi, on perd forcément la contrainte, et rien n’interdit que Raoul conduise un char Patton alors que ça lui est interdit.

    Pour empêcher cela, il suffit d’orienter dans le sens CONDUIRE vers INCLUSION, ce qui devient possible si on injecte l’attribut CategorieId dans CONDUIRE (en quoi serait-ce peccamineux ?) La contrainte est alors pleinement garantie.

    MLD correspondant :


    Nom : inclusion(looping)avecInclusion_MLD.png
Affichages : 663
Taille : 15,5 Ko

    Cela dit, eu égard au MCD original, la table CONDUIRE pourrait très bien comporter un attribut concernant la personne et le véhicule, par exemple date d’acquisition d’un véhicule par une personne, et manifestement, nonobstant son rôle fondamental, la table INCLUSION peut être phagocytée par la table INCLUSION, la contrainte est toujours préservée. Au fond, du fait de la contrainte, CONDUIRE peut être vue dans le MLD comme une association entre PSN_CAT et VEHICULE :


    Nom : inclusion(looping)avecInclusion_MLDexitINCLUSION.png
Affichages : 659
Taille : 12,8 Ko

    J’ai été bavard, j’ai beaucoup tourné autour du pot, à savoir autour de ce problème d’inclusion, mais j’essaie d’apporter une petite pierre à l’édifice, c’est-à-dire partant de la contrainte d’inclusion du MCD, arriver à voir comment produire mécaniquement ce dernier MLD qui me paraît pertinent. Est-ce faisable ? Je vous laisse juges.

    En aparté :  
    Une boulette de ma part, présente dans le post #6 :

    Citation Envoyé par fsmrel
    la table INCLUSION ne respecte pas la deuxième forme normale puisque l’attribut CategorieId dépend de l’attribut VehiculeId, donc d’une partie de la clé primaire
    J’étais manifestement fatigué.
    Cette fois-ci, pour vérifier la normalisation de la table CONDUIRE telle qu’elle est dans le dernier MLD, je rappelle la définition de la 3NF :

    La variable relationnelle R est en troisième forme normale (3NF) si et seulement si, pour chaque dépendance fonctionnelle non triviale X -> Y, au moins un des points suivants est vérifié :

    (a) X est une surclé.
    (b) Y est une sous-clé.

    Du fait de la dépendance fonctionnelle VehiculeId -> CategorieId, {CategorieId} est une sous-clé de la clé X = {PersonneId, CategorieId, VehiculeId} : la 3NF est vérifiée, mais pas la forme normale de Boyce Codd (BCNF), dans la définition de laquelle ne figure que le point (a) de la définition ci-dessus, et n’est donc satisfaite que si chaque DF non triviale est une surclé.

    Prochaine boulette à suivre...

  10. #10
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 397
    Points : 39 932
    Points
    39 932
    Billets dans le blog
    9
    Par défaut
    Citation Envoyé par fsmrel Voir le message
    Au vu du code SQL figurant dans le post #6, la manip n’est pas si simple : création d’une table INCLUSION pour traduire la contrainte d’inclusion, avec les nouvelles clés étrangères qui vont bien, sans oublier de supprimer celles qui sont devenues inutiles, donc pénalisantes.

    Pour ne pas en arriver là, à mon sens, la production du code SQL ne devrait pas nécessairement être effectuée directement à partir du MCD, mais au besoin, à partir du MLD ! Autrement dit, je dois avoir la main sur le MLD, qui d’entrée devrait faire figurer chaque colonne de chaque table, chaque clé (primaire, alternative). Partant de là, je dois pouvoir modifier ce MLD : mise en œuvre de la table INCLUSION, révision des clés en conséquence.
    D'accord avec le diagnostic.
    Concernant les conclusions, je ne serai pragmatique : si la personnalisation du MCD est possible sans dénaturer l'outil, c'est une excellente chose, sinon, faire les modifs dès le stade conceptuel, bien que ce soit théoriquement hors sujet à ce stade, ne me donne pas des insomnies.

  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 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Capitaine, tu auras noté qu’ici je ne suis pas sur le pont, dans le What, mais dans la soute, le How qui échappe à l’utilisateur. L’objectif est que l’AGL produise un code SQL dans lequel il a pris en compte la contrainte d’inclusion.

    Dan l’exemple, le code SQL produit par Looping est le suivant :

    CREATE TABLE PERSONNE
    (
       PersonneId INT,
       PersonneNom VARCHAR(32) NOT NULL,
       CONSTRAINT PERSONNE_PK PRIMARY KEY(PersonneId)
    );
    
    CREATE TABLE CATEGORIE
    (
       CategorieId INT,
       CategorieNom VARCHAR(32) NOT NULL,
       CONSTRAINT CATEGORIE_PK PRIMARY KEY(CategorieId),
       CONSTRAINT CATEGORIE_AK UNIQUE(CategorieNom)
    );
    
    CREATE TABLE PSN_CAT
    (
       CategorieId INT,
       PersonneId INT,
       PermisDate DATE NOT NULL,
       CONSTRAINT PSN_CAT_PK PRIMARY KEY(CategorieId, PersonneId),
       CONSTRAINT PSN_CAT_CATEGORIE_FK FOREIGN KEY(CategorieId) REFERENCES CATEGORIE(CategorieId),
       CONSTRAINT PSN_CAT_PERSONNE_FK FOREIGN KEY(PersonneId) REFERENCES PERSONNE(PersonneId)
    );
    
    CREATE TABLE VEHICULE
    (
       VehiculeId INT,
       VehiculeImmat VARCHAR(16) NOT NULL,
       CategorieId INT NOT NULL,
       CONSTRAINT VEHICULE_PK PRIMARY KEY(VehiculeId),
       CONSTRAINT VEHICULE_AK UNIQUE(VehiculeImmat),
       CONSTRAINT VEHICULE_CATEGORIE_FK FOREIGN KEY(CategorieId) REFERENCES CATEGORIE(CategorieId)
    );
    
    CREATE TABLE CONDUIRE
    (
       VehiculeId INT,
       CategorieId INT,
       PersonneId INT,
       CONSTRAINT CONDUIRE_PK PRIMARY KEY(VehiculeId, CategorieId, PersonneId),
       CONSTRAINT CONDUIRE_VEHICULE_FK FOREIGN KEY(VehiculeId) REFERENCES VEHICULE(VehiculeId),
       CONSTRAINT CONDUIRE_PSN_CAT_FK FOREIGN KEY(CategorieId, PersonneId) REFERENCES PSN_CAT(CategorieId, PersonneId)
    );
    MLD correspondant :

    Nom : inclusion(v3)afcet-figure b(fsmidrel)v3_exit_inclusion.png
Affichages : 644
Taille : 10,0 Ko

    Pour ma part, en tant qu’utilisateur, le code SQL me convient et je n’ai pas à me colleter des triggers.

    Un bémol quand même !
    Du fait de la dépendance fonctionnelle VehiculeId -> CategorieId, la clé primaire de la table CONDUIRE doit être réduite à la paire (PersonneId, VehiculeId).
    Le triplet (PersonneId, CategorieId, VehiculeId) est une surclé qu’on pourra dégager.

    En plus, au nom de la troisième forme normale il faudra garantir la dépendance fonctionnelle en cause (sinon risque de viol de DF), et là, c’est comment qu’on fait ? Ça sent le trigger...

    Evidemment, si la patte de l’association entre VEHICULE et CATEGORIE était remplacée par une cardinalité 0,N le problème évoqué ne se poserait plus, mais ceci est une autre histoire...

  12. #12
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Citation Envoyé par fsmrel
    au nom de la troisième forme normale il faudra garantir la dépendance fonctionnelle en cause (sinon risque de viol de DF), et là, c’est comment qu’on fait ? Ça sent le trigger...
    Bon, j’ai essayé avec un trigger (SQL Server) un peu rustique, mais ça devrait marcher :

    CREATE TRIGGER CONDUIRE_TRIGGER ON CONDUIRE
    INSTEAD OF INSERT, UPDATE
    AS
    DECLARE @Engueulade AS VARCHAR(512) ;
    DECLARE @n as INT ;
    
    SET @n = 
        (SELECT COUNT(*) 
         FROM  INSERTED AS x
         JOIN VEHICULE AS y 
            ON  x.VehiculeId = y.VehiculeId  
            AND  x.CategorieId <> y.CategorieId
        ) ;
    
    IF @N > 0
       BEGIN
         SET @Engueulade = 'Un véhicule n''appartient qu''à la catégorie qu''il détermine dans la table VEHICULE.'
         RAISERROR (@Engueulade, 16,1)  -- state = 16 pour bloquer  
         ROLLBACK  
       END;
    Les tables sont dans le message précédent.

    Caque paire (VehiculeId, CategorieId) de la table CONDUIRE doit préalablement exister dans la table VEHICULE.

  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 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Citation Envoyé par Escartefigue
    si la personnalisation du MCD est possible sans dénaturer l'outil, c'est une excellente chose.
    Message reçu 5/5.

    Je repars donc du MCD :
    Nom : inclusion(looping)fsm2.png
Affichages : 1245
Taille : 14,7 Ko

    Et j’utilise le code SQL produit par Looping :

    CREATE TABLE PERSONNE
    (
       PersonneId INT NOT NULL,
       PersonneNom VARCHAR(32) NOT NULL,
       CONSTRAINT PERSONNE_PK PRIMARY KEY(PersonneId)
    );
    
    INSERT INTO PERSONNE VALUES (1, 'Fernand') ;
    INSERT INTO PERSONNE VALUES (2, 'Raoul') ;
    INSERT INTO PERSONNE VALUES (3, 'Paul') ;
    
    SELECT * FROM PERSONNE ;
    
    CREATE TABLE CATEGORIE
    (
       CategorieId INT NOT NULL,
       CategorieNom VARCHAR(32) NOT NULL,
       CONSTRAINT CATEGORIE_PK PRIMARY KEY(CategorieId),
       CONSTRAINT CATEGORIE_AK UNIQUE(CategorieNom)
    );
    
    INSERT INTO CATEGORIE VALUES (1, 'categorie VL') ;
    INSERT INTO CATEGORIE VALUES (2, 'categorie PL') ;
    INSERT INTO CATEGORIE VALUES (3, 'char Patton') ;
    
    SELECT * FROM  CATEGORIE ;
    
    CREATE TABLE PSN_CAT
    (
       PersonneId INT NOT NULL,
       CategorieId INT NOT NULL,
       PermisDate DATE NOT NULL,
       CONSTRAINT PSN_CAT_PK PRIMARY KEY(PersonneId, CategorieId),
       CONSTRAINT PSN_CAT_CATEGORIE_FK FOREIGN KEY(CategorieId) REFERENCES CATEGORIE(CategorieId),
       CONSTRAINT PSN_CAT_PERSONNE_FK FOREIGN KEY(PersonneId) REFERENCES PERSONNE(PersonneId)
    );
    
    INSERT INTO PSN_CAT VALUES (1, 1, '1950-01-04') ;
    INSERT INTO PSN_CAT VALUES (1, 2, '1960-01-01') ;
    INSERT INTO PSN_CAT VALUES (1, 3, '1952-12-21') ;
    INSERT INTO PSN_CAT VALUES (2, 1, '1950-01-05') ;
    INSERT INTO PSN_CAT VALUES (2, 2, '1950-01-05') ;
    
    SELECT * FROM PSN_CAT ;
    
    CREATE TABLE VEHICULE
    (
       VehiculeId INT NOT NULL,
       CategorieId INT NOT NULL,
       VehiculeImmat VARCHAR(16) NOT NULL,
       CONSTRAINT VEHICULE_PK PRIMARY KEY(VehiculeId),
       CONSTRAINT VEHICULE_AK UNIQUE(VehiculeImmat),
       CONSTRAINT VEHICULE_CATEGORIE_FK FOREIGN KEY(CategorieId) REFERENCES CATEGORIE(CategorieId)
    );
    
    INSERT INTO VEHICULE VALUES (1, 1, '1A75') ;
    INSERT INTO VEHICULE VALUES (2, 1, '1Z44') ;
    INSERT INTO VEHICULE VALUES (3, 2, '1H91') ;
    INSERT INTO VEHICULE VALUES (4, 3, '9G38') ;
    
    SELECT * FROM VEHICULE ;
    
    CREATE TABLE CONDUIRE
    (
       PersonneId INT NOT NULL,
       VehiculeId INT  NOT NULL,
       CONSTRAINT CONDUIRE_PK PRIMARY KEY(PersonneId, VehiculeId),
       CONSTRAINT CONDUIRE_PSN_FK FOREIGN KEY(PersonneId) REFERENCES PERSONNE(PersonneId),
       CONSTRAINT CONDUIRE_VEH_FK FOREIGN KEY(VehiculeId) REFERENCES VEHICULE(VehiculeId)
    );
    Là encore, à mon corps défendant, je garantis la contrainte d’inclusion au moyen d’un trigger...

    CREATE TRIGGER CONDUIRE_TRIGGER ON CONDUIRE
    
    AFTER INSERT, UPDATE  
    AS
    DECLARE @Engueulade AS VARCHAR(512) ;
    DECLARE @n as INT ;
    
    SET @n = 
        (SELECT COUNT(*) 
         FROM  INSERTED AS x
         JOIN VEHICULE AS y 
            ON  x.VehiculeId = y.VehiculeId  
          JOIN PSN_CAT as z
            ON x.PersonneId = z.PersonneId
           AND y.CategorieId = z.CategorieId
        ) ;
    
    IF @N = 0
       BEGIN
         SET @Engueulade = '       Pour qu''une personne P puisse conduire un véhicule V qui appartient à une catégorie C,
         il faut qu''elle ait son permis dans cette catégorie.'
         RAISERROR (@Engueulade, 16,1)  -- state = 16 pour bloquer  
         ROLLBACK  
       END;
    go
    
    INSERT INTO CONDUIRE VALUES (1, 1) ;   -- psn  veh
    INSERT INTO CONDUIRE VALUES (1, 2) ;
    INSERT INTO CONDUIRE VALUES (1, 4) ;  --  psn  veh
    INSERT INTO CONDUIRE VALUES (2, 3) ;
    
    SELECT *   FROM CONDUIRE ;
    
    -- une tentative d'infraction
    
    INSERT INTO CONDUIRE VALUES (4, 3) ;
    =>

    Citation Envoyé par SQL Server
    Msg 50000, Niveau 16, État 1, Procédure CONDUIRE_TRIGGER, Ligne 23 [Ligne de départ du lot 111]
    Pour qu'une personne P puisse conduire un véhicule V qui appartient à une catégorie C,
    il faut qu'elle ait son permis dans cette catégorie.
    J’ai comme le sentiment que vous allez préférer cette solution...

  14. #14
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut Contrainte d'inclusion et trigger
    Le trigger que j’ai proposé fonctionne si les INSERT sont unitaires (une seule ligne à insérer à la fois). En prévision d’inserts de masse, il est préférable d’en utiliser un dans le genre de celui-ci :

    CREATE TRIGGER CONDUIRE_TRIGGER ON CONDUIRE
    AFTER INSERT, UPDATE
    AS
    DECLARE @Engueulade AS VARCHAR(512) ;
    DECLARE @n as INT ;
    SET @n = 
        (
         SELECT COUNT(*) 
         FROM  INSERTED AS x
         WHERE NOT EXISTS
           (
            SELECT * 
            FROM VEHICULE AS y
            JOIN PSN_CAT AS z ON x.PersonneId = z.PersonneId
            WHERE x.VehiculeId = y.VehiculeId 
              AND y.categorieid = z.categorieid 
           )
        ) 
    ;
    -- SELECT @n AS n
    IF @n >  0
       BEGIN
         SET @Engueulade = '       Pour qu''une personne P puisse conduire un véhicule V qui appartient à une catégorie C,
         il faut qu''elle ait son permis dans cette catégorie.'
         RAISERROR (@Engueulade, 16,1)  -- state = 16 pour bloquer  
         ROLLBACK  
       END;
    Insert délinquant (2, 4) :
    INSERT INTO CONDUIRE VALUES
       (1, 1)    -- psn  veh
     , (1, 2) 
     , (1, 4) 
     , (2, 3)
     , (2, 4)  -- délinquant
     ; 

  15. #15
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 397
    Points : 39 932
    Points
    39 932
    Billets dans le blog
    9
    Par défaut
    Citation Envoyé par fsmrel Voir le message
    J’ai comme le sentiment que vous allez préférer cette solution...
    En effet

  16. #16
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Capitaine, puisque tu préfères la solution sans modification du code SQL produit par Looping, d’accord, je me remets à l’étude des triggers (avec SQL Server pour commencer), à chaque jour suffit sa peine.

    Je reviens sur l’exemple proposé par Yves Tabourier, dans le post #1, dans lequel un professeur ne peut enseigner une matière à une classe que s’il est qualifié pour cela. Je reprends cet exemple parce qu’il est aussi présent dans l’ouvrage de Dominique Nanci et Bernard Espinasse, Ingénierie des Systèmes d’information : Merise, au chapitre 13, page 305 et qu’on y cause de triggers pour garantir la contrainte. 

    Chez DVP qui a repris l’ouvrage : « III-C-3-ac. Contraintes d'inclusion de relations sur d'autres relations ».

    Le MCD proposé dans l’ouvrage :

    Nom : Nanci_inclusion_mcd.png
Affichages : 622
Taille : 25,0 Ko

    On y trouve donc un trigger pour garantir la contrainte d’inclusion :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CREATE TRIGGER Inclusion_Enseigner_Qualifier
    BEFORE INSERT ON Enseigner
      ON EACH ROW
      DECLARE
          est_qualifié number;
      BEGIN
          SELECT COUNT(*) INTO est_qualifié FROM Qualifier
          WHERE n°professeur = :new.n°professeur
                AND n°matière = :new.n°matière;
          IF est_qualifié = 0 THEN
              raise_application_error (-20007, "Ce professeur n'est pas qualifié pour cette matière" ));
          END IF;
      END;
    So far so good, mais la contrainte peut malgré tout être violée ! En effet, que se passe-t-il quand le professeur Untel perd une ou plusieurs de ses qualifications ? Il n’y a aucun impact sur la table ENSEIGNER...

    Moralité : la table QUALIFIE doit elle aussi avoir ses petits triggers...

    Allez, je me fends d’un code pour remédier à cela, tout en sachant qu’il devra y avoir des triggers FOR UPDATE, mais comme je l’ai dit plus haut, à chaque jour suffit sa peine.

    Je propose le code suivant (SQL Server)

    CREATE TABLE MATIERE
    (
        NoMatiere  INT NOT NULL
      , NomMatiere VARCHAR(24) NOT NULL
      , CONSTRAINT MATIERE_PK PRIMARY KEY(NoMatiere)
    ) ;
    
    CREATE TABLE PROFESSEUR
    (
        NoProfesseur  INT NOT NULL
      , NomProfesseur VARCHAR(24) NOT NULL
      , CONSTRAINT PROFESSEUR_PK PRIMARY KEY(NoProfesseur)
    ) ;
    
    CREATE TABLE CLASSE
    (
        NoClasse  INT NOT NULL
      , NomClasse  VARCHAR(24) NOT NULL
      , CONSTRAINT CLASSE_PK PRIMARY KEY(NoClasse)
    ) ;
    
    CREATE TABLE QUALIFIE
    (
        NoMatiere  INT NOT NULL
      , NoProfesseur INT NOT NULL
      , CONSTRAINT QUALIFIE_PK PRIMARY KEY(NoMatiere, NoProfesseur)
      , CONSTRAINT QUAL_MAT_FK FOREIGN KEY(NoMatiere) 
            REFERENCES MATIERE (NoMatiere)
            ON DELETE CASCADE
      , CONSTRAINT QUAL_PROF_FK FOREIGN KEY(NoProfesseur) 
            REFERENCES PROFESSEUR (NoProfesseur)
    ) ;
    
    CREATE TABLE ENSEIGNER
    (
        NoMatiere  INT NOT NULL
      , NoProfesseur INT NOT NULL
      , NoClasse INT NOT NULL
      , CONSTRAINT ENSEGNER_PK PRIMARY KEY(NoMatiere, NoProfesseur, NoClasse)
      , CONSTRAINT ENS_MAT_FK FOREIGN KEY(NoMatiere) 
            REFERENCES MATIERE (NoMatiere)
            ON DELETE CASCADE
      , CONSTRAINT ENS_PROF_FK FOREIGN KEY(NoProfesseur) 
            REFERENCES PROFESSEUR (NoProfesseur)
      , CONSTRAINT ENS_CLASSE_FK FOREIGN KEY(NoClasse) 
            REFERENCES CLASSE (NoClasse)
    ) ;
    Quelques inserts

    INSERT INTO MATIERE VALUES
        (1, 'français')
      , (2, 'maths')
      , (3, 'histoire')
      , (4, 'géographie')
      , (5, 'latin')
      , (6, 'physique')
      , (7, 'chimie')
      , (8, 'philo')
      , (9, 'anglais')
      , (10, 'allemand')
      , (11, 'informatique')
      , (12, 'grec')
     ;
    SELECT * FROM MATIERE ; 
    
    INSERT INTO PROFESSEUR VALUES
        (1, 'Fernand')
      , (2, 'Raoul')
      , (3, 'Paul')
      , (4, 'Antoine')
      , (5, 'Mimile')
      , (6, 'Jean')
      , (7, 'Theo')
      , (8, 'Fred')
      , (9, 'Patrick')
    ;
    SELECT * FROM PROFESSEUR ;
    
    INSERT INTO CLASSE VALUES
        (1, 'Mathelem')
      , (2, 'Science ex')
      , (3, 'Philo')
      , (4, '1ere A')
      , (5, '1ere B') 
      , (6, '1ere C') 
      , (7, '2nde A')
      , (8, '2nde B') 
      , (9, '2nde C') 
    ;
    SELECT * FROM CLASSE ;
    
    INSERT INTO QUALIFIE VALUES
        (1, 2)  -- français     Raoul
      , (1, 4)  -- français     Antoine      
      , (1, 5)  -- français     Mimile      
      , (3, 1)  -- histoire     Fernand
    --  , (3, 6)  -- histoire     Jean
      , (4, 1)  -- géo          Fernand
      , (5, 2)  -- latin        Raoul
      , (6, 7)  -- physique     Theo      
      , (7, 2)  -- chimie       Raoul
      , (7, 7)  -- chimie       Theo      
      , (8, 1)  -- philo        Fernand
      , (9, 6)  -- anglais      Jean
      , (10, 6) -- allemand     Jean
      , (11, 8)  -- informatique Fred       
      , (11, 9)  -- informatique Patrick       
      , (12, 3)  -- grec         Paul
    ;
    SELECT NomMatiere, NomProfesseur, '' as matprof
        FROM QUALIFIE AS q
        JOIN  MATIERE AS m ON q.NoMatiere = m.NoMatiere
        JOIN PROFESSEUR AS p ON q.NoProfesseur = p.NoProfesseur 
    ;
    Un trigger pour la table ENSEIGNER :

    CREATE TRIGGER INCLUSION_ENSEIGNER_TRIGGER_INSERT ON ENSEIGNER
    AFTER INSERT
    AS
    DECLARE @Engueulade AS VARCHAR(512) ;
    DECLARE @n as INT ;
    DECLARE @nbInserted as INT ; 
    
    -- pour y voir clair
    
    SELECT''  AS ENS_ISRT_trigger, *
    FROM INSERTED
    ORDER BY NoMatiere, NoProfesseur, NoClasse
    SET @n = 
        (
         SELECT COUNT(*) 
         FROM  INSERTED AS e
         WHERE NOT EXISTS
           (
            SELECT * 
            FROM  QUALIFIE AS q
            JOIN CLASSE AS c ON  e.NoClasse = c.NoClasse
            WHERE e.NoMatiere = q.NoMatiere
              and e.NoProfesseur = q.NoProfesseur
           )
        ) 
    ;
    SELECT @n AS nbViols
    
    IF @n >  0
       BEGIN
         SELECT '' AS Les_delinquants, *
         FROM INSERTED AS e
         WHERE NOT EXISTS
           (
            SELECT * 
            FROM  QUALIFIE AS q
            JOIN CLASSE AS c ON  e.NoClasse = c.NoClasse
            WHERE e.NoMatiere = q.NoMatiere
              AND e.NoProfesseur = q.NoProfesseur
           )
     
         SET @Engueulade = 'Insert into ENSEIGNER, Viol de la contrainte d''inclusion.'
         RAISERROR (@Engueulade, 16,1)  -- state = 16 pour bloquer  
         ROLLBACK  
       END;
    
    Et un trigger pour la table QUALIFIE :

    CREATE TRIGGER INCLUSION_QUALIFIE_TRIGGER_DELETE ON QUALIFIE
    AFTER DELETE
    AS
    DECLARE @Engueulade AS VARCHAR(512) ;
    DECLARE @n as INT ;
    
    SELECT *, ''  AS deleted FROM DELETED
    ORDER BY NoMatiere, NoProfesseur
    
    SET @n = 
        (
         SELECT COUNT(*) 
         FROM  ENSEIGNER AS e
         WHERE EXISTS
           (
            SELECT * 
            FROM  DELETED AS d
            WHERE d.NoMatiere = e.NoMatiere
              and d.NoProfesseur = e.NoProfesseur
           )
        ) 
    ;
     SELECT @n AS nDel ;
    
    IF @n >  0
       BEGIN
         SELECT '' as Delinquant, *
         FROM  ENSEIGNER AS e
         WHERE EXISTS
           (
            SELECT * 
            FROM  DELETED AS d
            WHERE d.NoMatiere = e.NoMatiere
              and d.NoProfesseur = e.NoProfesseur
           )
    
         SET @Engueulade = 'Delete From QUALIFIE, viol de la contrainte d''inclusion.'
         RAISERROR (@Engueulade, 16,1)  -- state = 16 pour bloquer  
         ROLLBACK  
       END;
    
    Quelques lignes dans la table ENSEIGNER :

    INSERT INTO ENSEIGNER VALUES
        (1, 4, 6)   -- français         Antoine     1ere C      
      , (1, 4, 8)   -- français         Antoine     2nde B
      , (1, 5, 4)   -- français         Mimile      1re A
      , (1, 5, 8)   -- français         Mimile      2nde B  
      , (3, 1, 1)  --  histoire         Fernand     mathelem
      , (4, 1, 1)  --  géo              Fernand     mathelem
      , (4, 1, 3)  --- géo              Fernand     philo
      , (5, 2, 4)  --  latin            Raoul       1re A  
      , (6, 7, 4)   -- physique         Theo        1re A    
      , (7, 2, 1)  --  chimie            Raoul      mathelem  
      , (7, 2, 3)  - - chimie            Raoul      philo
      , (7, 7, 4)  --  chimie           Theo        1re A    
      , (7, 7, 5)  - - chimie           Theo        1re B    
      , (9, 6, 3)  --  anglais          Jean        philo
      , (11, 8, 3) --  informatique     Fred        philo       
      , (11, 8, 2) --  informatique     Fred        science ex      
      , (11, 9, 2) --  informatique     Patrick     science ex 
      , (11, 9, 3) --  informatique     Patrick     philo    
    ;
    Avec le DELETE qui suit, le trigger constatera le viol de la contrainte d'inclusion : Fernand n’enseignerait plus la géo alors qu'il l'enseigne aux classes de Mathelem et Philo.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    DELETE FROM QUALIFIE WHERE NoProfesseur = 1 AND NoMatiere = 4
    Réaction du SGBD

    Citation Envoyé par SQL Server
    Msg 50000, Niveau 16, État 1, Procédure INCLUSION_QUALIFIE_TRIGGER_DELETE
    Delete From QUALIFIE, viol de la contrainte d'inclusion.
    Le constat de la culpabilité :

    Delinquant    NoMatiere    NoProfesseur    NoClasse
                          4               1           1
                          4               1           3 
    Tout cela aux erreurs de copier/coller près.

    Comme annoncé, manquent les triggers FOR UPDATE.

    Il est évident que le plus simple eut été de déclarer une étrangère dans le CREATE TABLE de la table ENSEIGNER (cf. post #1) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     CREATE TABLE ENSEIGNER
    (
        NoMatiere INT
      , NoProfesseur INT
      , NoClasse INT
      , CONSTRAINT ENSEGNER_PK PRIMARY KEY(NoMatiere, NoProfesseur, NoClasse)
      , CONSTRAINT ENSEIGNER_QUALIFIE_FK FOREIGN KEY(NoMatiere, NoProfesseur) 
          REFERENCES QUALIFIE (NoMatiere, NoProfesseur),
      , CONSTRAINT ENS_CLASSE_FK FOREIGN KEY(NoClasse) 
          RE(FERENCES CLASSE (NoClasse)
    );
    Auquel cas exeunt les triggers…

  17. #17
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 397
    Points : 39 932
    Points
    39 932
    Billets dans le blog
    9
    Par défaut
    Bonjour François,

    Si la qualification d'un enseignant peut se périmer, alors le MCD présenté n'est pas conforme, il manque la période de validité de la qualification. Il faut donc une asso ternaire pour répondre à cette nouvelle règle de gestion .

  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 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Bonjour Capitaine,

    Il est évident que dans le monde réel le temps est partie prenante : si Raoul quitte le Lycée Papillon on doit se souvenir qu’il y a exercé de la date D1 à la date D2 (le DURING du modèle relationnel de données) et que celui qui le relaie exerce à partir disons de D2 + 1 (le SINCE du modèle relationnel de données), cf. la sixième forme normale et tout le fourbi.

    Mon intention dans cette discussion n’est pas de traiter d’un cas particulier, mais du cas général dans lequel le temps n’a pas de raison particulière de s’inviter. Les variables sont X,Y,Z plutôt que X,Y,Z,T...

    Nom : inclusion(x,y,z).png
Affichages : 606
Taille : 6,4 Ko

  19. #19
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 397
    Points : 39 932
    Points
    39 932
    Billets dans le blog
    9
    Par défaut
    OK, quoi qu'il en soit, une contrainte d'inclusion se traduit le plus souvent par une contrainte SQL de type "reference" qui ne saurait vérifier qu'une période est incluse dans une autre, un trigger s'imposera de toute façon ici.

  20. #20
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 121
    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 121
    Points : 31 642
    Points
    31 642
    Billets dans le blog
    16
    Par défaut
    Pour compléter, voici les triggers contrôlant les UPDATE.

    Table QUALIFIE :

    CREATE TRIGGER INCLUSION_QUALIFIE_TRIGGER_UPDATE ON QUALIFIE
    AFTER UPDATE
    AS
    DECLARE @Engueulade AS VARCHAR(512) ;
    DECLARE @n as INT ;
    SET @n = 
        (
         SELECT COUNT(*)
         FROM   DELETED AS e
         WHERE  EXISTS 
            (
             SELECT * FROM ENSEIGNER AS q 
             WHERE q.NoMatiere = e.NoMatiere
               AND q.NoProfesseur = e.NoProfesseur
            )
        )
    ;
    IF @n >  0
       BEGIN
         SELECT '' AS Opposant, *
         FROM   DELETED AS e
         WHERE  EXISTS 
            (SELECT * FROM ENSEIGNER AS q 
             WHERE q.NoMatiere = e.NoMatiere
               AND q.NoProfesseur = e.NoProfesseur)
         ;
         SET @Engueulade = 'Update QUALIFIE, viol de la contrainte d''inclusion, veto de ENSEIGNER.'
         RAISERROR (@Engueulade, 16,1)  -- state = 16 pour bloquer  
         ROLLBACK
       END;
    Table ENSEIGNER :

    CREATE TRIGGER INCLUSION_ENSEIGNER_TRIGGER_UPDATE ON ENSEIGNER
    AFTER UPDATE
    AS
    DECLARE @Engueulade AS VARCHAR(512) ;
    DECLARE @n as INT ;
    SET @n = 
        (
         SELECT COUNT(*)
         FROM   INSERTED AS e
         WHERE  NOT EXISTS 
            (
             SELECT * FROM QUALIFIE AS q 
             WHERE q.NoMatiere = e.NoMatiere
               AND q.NoProfesseur = e.NoProfesseur
            )
        )
    ;
    IF @n > 0
       BEGIN
         SELECT '' AS Delinquant, *
         FROM   INSERTED AS e
         WHERE  NOT EXISTS 
            (SELECT * FROM QUALIFIE AS q 
             WHERE q.NoMatiere = e.NoMatiere
               AND q.NoProfesseur = e.NoProfesseur)
         ;
         SET @Engueulade = 'Update ENSEIGNER, viol de la contrainte d''inclusion, veto de QUALIFIE.'
         RAISERROR (@Engueulade, 16,1)  -- state = 16 pour bloquer  
         ROLLBACK
       END ;

Discussions similaires

  1. Prise en compte des contraintes
    Par potanie dans le forum Décisions SGBD
    Réponses: 1
    Dernier message: 05/11/2004, 11h00
  2. heritage des contraintes
    Par krimson dans le forum PostgreSQL
    Réponses: 3
    Dernier message: 30/04/2004, 13h04
  3. Affichage des contraintes
    Par nicobouboufr dans le forum SQL Procédural
    Réponses: 3
    Dernier message: 17/03/2004, 10h21
  4. Au sujet des mots de passe
    Par FranT dans le forum Langage
    Réponses: 6
    Dernier message: 17/09/2002, 23h16
  5. Au sujet des constantes
    Par FranT dans le forum Langage
    Réponses: 8
    Dernier message: 09/08/2002, 12h03

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