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

Langage SQL Discussion :

Besoin de conseils : Trigger ou autre chose ?


Sujet :

Langage SQL

  1. #1
    Membre habitué Avatar de touftouf57
    Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2007
    Messages
    362
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2007
    Messages : 362
    Points : 174
    Points
    174
    Par défaut Besoin de conseils : Trigger ou autre chose ?
    Bonjour à tous,

    J'aimerais savoir comment vous gérez cette situation:
    Table Classe:idClasse,...
    Table Eleve:idEleve,idClasse....,Représentant(booléen)

    Je voudrais m'assurer qu'il n'y ai qu'un seul Eleve qui soit représentant d'une classe (booléen Representant=true).
    Je pensais pouvoir m'en sortir avec des triggers mais cela me donne des comportements étranges puisque le trigger s’exécute sur chaque ligne updatée. Donc à un instant t (trigger sur 1° ligne updatée), je pourrais avoir 2 représentants et à t+1 (trigger sur 2° ligne) en avoir qu'un. Tout comme à un instant t, n'avoir plus de représentant et à t+1 en avoir de nouveau 1.

    Donc, le contrôle devrait plutôt se faire dans une transaction après les modifications. Cependant, je ne vois pas quel "outil" pourrait convenir.

    Je voudrais savoir quel type de contrôle je pourrais bien utiliser, à moins que ceci ne soit pas réalisable dans la BDD et donc que je dois plutôt l'implémenter dans l'application.
    Sinon, il me reste la solution de mettre la référence du représentant dans la table CLASSE.


    Merci d'avance pour vos pistes.

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


    Citation Envoyé par touftouf57 Voir le message
    Il me reste la solution de mettre la référence du représentant dans la table CLASSE.
    Certes, mais le problème est que vous auriez un cycle, et qu’en plus les clés étrangères ne permettraient pas d’empêcher la situation dans laquelle un élève d’une classe peut représenter une autre classe...

    CLASSE                          ELEVE
    +-----------+-----------+       +----------+------------+
    | IdClasse  |  IdEleve  |       | IdEleve  |  IdClasse  |
    |-----------|-----------|       |----------|------------|
    | c1        |  e1       |       | e1       |  c2        |
    | c2        |  e2       |       | e2       |  c1        |
    +-----------+-----------+       +----------+------------+ 
    En relationnel pur, il n’y a pas de problème pour empêcher cela, grâce à la déclaration des contraintes et à l’affectation multiple (plusieurs inserts dans un même paquet avant déclenchement des contrôles, par exemple création en même temps de la classe et de son représentant), mais en SQL vous retomberez dans les problèmes de l’affectation unique et de la mise en œuvre de triggers pour résoudre le problème de l’élève représentant d’une classe qui n’est pas la sienne.

    Dans le contexte SQL, et vu votre peu d'engouement pour les triggers, je vous propose :

    1) Pour casser le cycle, de définir une table REPRESENTANT connectant CLASSE et ELEVE dans le cadre de la représentation.

    2) Pour éviter la programmation de triggers garantissant qu’un représentant d’une classe fait bien partie de celle-ci, je vous propose une astuce vaseuse (mais efficace), qu’en relationnel pur je me refuserai de mettre en œuvre, mais excusable en SQL. Cette astuce figure dans le script ci-dessous (MS SQL Server) :

    Table CLASSE
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    CREATE TABLE CLASSE
    (
            C         CHAR(3)        NOT NULL
          , Cnom      VARCHAR(64)    NOT NULL
        , CONSTRAINT CLASSE_PK PRIMARY KEY (C)
    ) ;

    TABLE ELEVE
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    CREATE TABLE ELEVE
    (
            E         CHAR(3)        NOT NULL
          , Enom      VARCHAR(64)    NOT NULL
          , C         CHAR(3)        NOT NULL
        , CONSTRAINT ELEVE_PK PRIMARY KEY (E)
        , CONSTRAINT ELEVE_AK UNIQUE (E, C)        -- astuce vaseuse (début)
        , CONSTRAINT ELEVE_FK FOREIGN KEY (C) 
             REFERENCES CLASSE
    ) ;

    TABLE REPRESENTANT
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    CREATE TABLE REPRESENTANT
    (
            C         CHAR(3)        NOT NULL
          , E         CHAR(3)        NOT NULL
        , CONSTRAINT REPRESENTANT_CLASSE_PK PRIMARY KEY (C)
        , CONSTRAINT REPRESENTANT_ELEVE_PK UNIQUE (E)
        , CONSTRAINT REPRESENTANT_CLASSE_FK FOREIGN KEY (C) 
              REFERENCES CLASSE
        , CONSTRAINT REPRESENTANT_ELEVE_FK FOREIGN KEY (E, C) 
              REFERENCES ELEVE (E, C)           -- astuce vaseuse (fin)
    ) ;


    Un bout de jeu d’essai

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    INSERT INTO CLASSE (C, Cnom) VALUES ('c1', 'classe 1') ;
    INSERT INTO CLASSE (C, Cnom) VALUES ('c2', 'classe 2') ;
    INSERT INTO CLASSE (C, Cnom) VALUES ('c3', 'classe 3') ;
    INSERT INTO CLASSE (C, Cnom) VALUES ('c4', 'classe 4') ;
     
    INSERT INTO ELEVE (E, Enom, C) VALUES ('e1', 'Fernand', 'c1') ;
    INSERT INTO ELEVE (E, Enom, C) VALUES ('e2', 'Jean', 'c1') ;
    INSERT INTO ELEVE (E, Enom, C) VALUES ('e3', 'Raoul', 'c2') ;
    INSERT INTO ELEVE (E, Enom, C) VALUES ('e4', 'Paul', 'c2') ;
    INSERT INTO ELEVE (E, Enom, C) VALUES ('e5', 'Pascal', 'c2') ;
     
    INSERT INTO REPRESENTANT (C, E) VALUES ('c1', 'e1') ;
    INSERT INTO REPRESENTANT (C, E) VALUES ('c2', 'e2') ;     -- Boum !

    Non seulement Jean et Fernand ne peuvent pas être en même temps représentants de la classe c1, mais clairement, le SGBD rouspète quand on cherche à désigner Jean comme représentant de la classe c2 qui n’est pas la sienne...

  3. #3
    Membre habitué Avatar de touftouf57
    Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2007
    Messages
    362
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2007
    Messages : 362
    Points : 174
    Points
    174
    Par défaut
    Merci fsmrel pour votre réponse

    Dans le contexte SQL, et vu votre peu d'engouement pour les triggers
    Je me suis probablement mal exprimé. Je ne suis pas contre l'utilisation de trigger.

    Je n'avais pas vu que ma solution de déporter le représentant dans la table ELEVE me générait un cycle. Alors qu'une fois écrit, cela saute aux yeux.

    Je vais tester votre solution. Cependant, si avec des triggers je peux arriver à mes fins, je préfèrerai.

    Merci de votre aide.

    PS: Je ne vois pas l'équivalent MCD de votre solution. Pourriez-vous me l'expliquer? Merci d'avance

  4. #4
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Points : 13 092
    Points
    13 092
    Par défaut
    Bonjour,

    Vous n'avez pas précisé votre SGBDR, mais si celui-ci accepte les index filtrés (par exemple SQL Server 2008 et + ), vous pouvez, à partir de votre modèle initial, simplement créer un index unique filtré sur Représentant = 1 :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    CREATE UNIQUE INDEX UIX_ELEVE_REPRESENTANT
        ON ELEVE(IdClasse)
        WHERE Représentant = 1

  5. #5
    Membre habitué Avatar de touftouf57
    Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2007
    Messages
    362
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2007
    Messages : 362
    Points : 174
    Points
    174
    Par défaut
    Oups!!
    La base de données est RDB.

  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 119
    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 119
    Points : 31 627
    Points
    31 627
    Billets dans le blog
    16
    Par défaut
    Bonsoir touftouf57,


    En fait, la contrainte référentielle REPRESENTANT_ELEVE_FK suffit pour remplir le rôle assumé par la contrainte référentielle REPRESENTANT_CLASSE_FK qui devient inutile et disparaît donc :

    TABLE REPRESENTANT
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    CREATE TABLE REPRESENTANT
    (
            C         CHAR(3)        NOT NULL
          , E         CHAR(3)        NOT NULL
        , CONSTRAINT REPRESENTANT_CLASSE_PK PRIMARY KEY (C)
        , CONSTRAINT REPRESENTANT_ELEVE_PK UNIQUE (E)
        , CONSTRAINT REPRESENTANT_ELEVE_FK FOREIGN KEY (E, C) 
              REFERENCES ELEVE (E, C)           -- astuce vaseuse (fin)
    ) ;

    Le MCD (merisement correct) correspondant est le suivant, avec spécialisation du représentant de la classe (l’AGL est PowerAMC) :




    Pour avoir le moins possible à bricoler le MLD qui en sera dérivé, on peut partir d’un MCD sémantiquement légèrement différent (l’élève devient un élément de la classe, une entité faible), grâce à l'utilisation de l’identification relative :





    Le MLD produit par PowerAMC :



    Il faut évidemment doter la table ELEVE de la clé candidate {E} et dans la table REPRESENTANT remplacer la clé candidate {C, E} par les des deux clés candidates {C} et {E} :




    Citation Envoyé par touftouf57 Voir le message
    La base de données est RDB.
    RDB, le seul SGBDR qui dans les années quatre-vingts savait ce qu’était un domaine, un des concepts au fondement de la théorie relationnelle inventée par Codd en 1969-1970...

  7. #7
    Membre habitué Avatar de touftouf57
    Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2007
    Messages
    362
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2007
    Messages : 362
    Points : 174
    Points
    174
    Par défaut
    Merci beaucoup fsmrel pour ces éclaircissements en terme de structure de BDD.

    Sinon, vous auriez fait comment avec des triggers? A moins que cela ne soit pas possible?

  8. #8
    Membre habitué Avatar de touftouf57
    Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2007
    Messages
    362
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2007
    Messages : 362
    Points : 174
    Points
    174
    Par défaut
    fsmrel, cela soulève un autre problème.

    Désormais lorsque je désire faire référence à un élève pour lui attribuer une note, je dois donner la clé primaire complète E et C.
    Cela est très génant.

  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 119
    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 119
    Points : 31 627
    Points
    31 627
    Billets dans le blog
    16
    Par défaut
    Bonjour touftouf,


    Je vais essayer de trouver un moment pour les triggers.


    Quant à la clé primaire, ne vous fiez pas à PowerAMC (qui ne sert que pour le plus gros du travail), mais à la réalité de la base de données, selon laquelle la clé primaire de la table ELEVE est seulement composée de la colonne C, revoyez la structure de cette table dans mon 1er message. La paire {E, C} n'est que clé alternative (clause UNIQUE de la contrainte ELEVE_AK), servant de référence pour la clé étrangère {E, C} de la table REPRESENTANT.

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


    Citation Envoyé par touftouf57 Voir le message
    Vous auriez fait comment avec des triggers ? A moins que cela ne soit pas possible ?
    Si je vous suis, il s’agit de remplacer la table REPRESENTANT par un booléen (appelons-le « Repr ») dans la table ELEVE. La partie correspondante de la base de données devient celle-ci :


    MCD




    Script SQL

    Table CLASSE
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    CREATE TABLE CLASSE
    (
            C         CHAR(3)        NOT NULL
          , Cnom      VARCHAR(64)    NOT NULL
        , CONSTRAINT CLASSE_PK PRIMARY KEY (C)
    ) ;

    TABLE ELEVE
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    CREATE TABLE ELEVE
    (
            E         CHAR(3)        NOT NULL
          , Enom      VARCHAR(64)    NOT NULL
          , C         CHAR(3)        NOT NULL
          , Repr      INT            NOT NULL DEFAULT 0
        , CONSTRAINT ELEVE_PK PRIMARY KEY (E)
        , CONSTRAINT ELEVE_FK FOREIGN KEY (C) REFERENCES CLASSE
        , CONSTRAINT ELEVE_Chk_Repr CHECK (Repr IN (1, 0))
    ) ;
    Où la colonne Repr ne peut prendre que les valeurs 1 et 0 (l’élève est ou n’est pas le représentant de sa classe). Il s’agit donc de s’assurer par trigger qu’il n’y a qu’un seul représentant par classe.

    Je n’ai pas RDB, donc je serais bien en peine de vous proposer les triggers exacts qui vous intéressent. Tout au plus puis-je vous fournir une version dans le style de MS SQL Server.

    Trigger (SQL Server) INSERT affectant la table ELEVE

    Par exemple, pour garantir la contrainte lors d’un INSERT, le trigger correspondant peut compter le nombre de fois que la valeur 1 est fournie pour chaque classe dans la table ELEVE avant INSERT (nombre qui ne devrait pas être supérieur à 1 !), même chose quant à la table temporaire (nommée INSERTED dans le cas de SQL Server) qui contient les lignes candidates à insertion, donc potentiellement un nombre quelconque de candidats représentants :

    SELECT portant sur la table ELEVE :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        SELECT C 
        FROM   ELEVE 
        WHERE  Repr = 1

    SELECT portant sur la table temporaire :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        SELECT C 
        FROM   INSERTED
        WHERE  Repr = 1

    En assemblant les deux SELECT :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        SELECT C 
        FROM   ELEVE 
        WHERE  Repr = 1 
       UNION ALL
        SELECT C 
        FROM   INSERTED
        WHERE  Repr = 1

    Le but de la manœuvre est de s’assurer qu’au final on n’a pas plus d’un représentant par classe, et pour cela on peut effectuer un comptage par classe en complétant ainsi :
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        SELECT COUNT(C)
        FROM
            (
             SELECT C 
             FROM   ELEVE
             WHERE  Repr = 1
            UNION ALL
             SELECT C 
             FROM   INSERTED
             WHERE  Repr = 1
            ) AS x
        GROUP BY C

    Pour mettre en évidence qu’au moins une classe compterait — à tort — en tout plus d’un représentant, on peut enrichir ainsi la requête :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
        SELECT MAX(S)
        FROM
            (
             SELECT COUNT(C)
             FROM
                 (
                  SELECT C 
                  FROM   ELEVE
                  WHERE  Repr = 1
                 UNION ALL
                  SELECT C 
                  FROM   INSERTED
                  WHERE  Repr = 1
                 ) AS x
             GROUP BY C
            ) AS x

    Le trigger complet en SQL Server serait le suivant (aux erreurs de copier/coller près) :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    CREATE TRIGGER ELEVE_TRIGGER_INSERT ON ELEVE INSTEAD OF INSERT AS
        DECLARE @N AS INT ; 
     
        SET @N  = 
            (
             SELECT MAX(S)
             FROM
                 (
                  SELECT COUNT(C) AS S
                  FROM
                      (
                       SELECT C 
                       FROM   ELEVE
                       WHERE  Repr = 1
                      UNION ALL
                       SELECT C 
                       FROM   INSERTED
                       WHERE  Repr = 1
                      ) AS x
                  GROUP BY C
                 ) AS x 
            ) ;  
     
        IF @N > 1
            /* ---------------------------
                  Infraction détectée
            */ ---------------------------
            BEGIN
                RAISERROR ('INSERT ELEVE - Un seul représentant par classe !', 16, 1)
                ROLLBACK TRAN
                RETURN
            END
        /* -----------------------------------------------------
             Si pas d'infraction, on peut effectuer l’insert
        */ -----------------------------------------------------
        INSERT INTO ELEVE (E, Enom, C, Repr)
            SELECT E, Enom, C, Repr
            FROM   INSERTED ;


    Trigger pour DELETE affectant la table ELEVE

    Il est possible d’empêcher la suppression d’un représentant (disons tant qu’il n’a pas d’abord été remplacé) :


    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    CREATE TRIGGER ELEVE_TRIGGER_DELETE ON ELEVE INSTEAD OF DELETE AS
        DECLARE @N AS INT ;
     
        SET @N = ( 
                  SELECT COUNT(*)
                  FROM   DELETED 
                  WHERE  Repr = 1
                 ) 
     
        IF @N > 0
            /* ---------------------------
                  Infraction détectée
            */ ---------------------------
            BEGIN
                RAISERROR ('Avant de supprimer un représentant de classe, commencer par en désigner un autre.', 16, 1)
                ROLLBACK TRAN
                RETURN
            END
        /* -----------------------------------------------------
             Si pas d'infraction, on peut effectuer l'opération
        */ -----------------------------------------------------
        DELETE FROM ELEVE
               WHERE E IN (
                           SELECT E
                           FROM   DELETED
                          ) ;


    Trigger pour UPDATE affectant la table ELEVE

    L’embêtant avec SQL est que la structure de l’instruction UPDATE ne permet pas d’affecter en une fois le rôle de représentant d’un élève à un autre, aussi je pense que le moyen les plus simple est de travailler en deux temps. Paer exemple, si l’élève e1 représente sa classe et que désormais c’est au tour de l’élève e2, on aura deux UPDATE :


    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    UPDATE ELEVE
        SET Repr = 0 WHERE E  = 'e1' ;
     
    UPDATE ELEVE
        SET Repr = 1 WHERE E  = 'e2' ;

    Le trigger (je ne fais pas dans la dentelle...) :


    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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
    CREATE TRIGGER ELEVE_TRIGGER_UPDATE ON ELEVE INSTEAD OF UPDATE AS
        DECLARE @N AS INT
     
        SET @N  = 
            (
             SELECT MAX(S)
             FROM
                 (
                  SELECT COUNT(C) AS S
                  FROM
                      (
                       SELECT C 
                       FROM   ELEVE
                       WHERE  Repr = 1
                      UNION ALL
                       SELECT C 
                       FROM   INSERTED
                       WHERE  Repr = 1
                      ) AS x
                  GROUP BY C
                 ) AS x 
            ) ;  
     
        IF @N > 1
            /* ---------------------------
                  Infraction détectée
            */ ---------------------------
            BEGIN
                RAISERROR ('UPDATE ELEVE - Un seul représentant par classe !', 16, 1)
                ROLLBACK TRAN
                RETURN
            END
        /* -----------------------------------------------------
             Si pas d'infraction, on peut effectuer l'opération
        */ -----------------------------------------------------
     
        DELETE FROM ELEVE
               WHERE E IN (
                           SELECT E
                           FROM   DELETED
                          ) ;
     
        INSERT INTO ELEVE (E, Enom, C, Repr)
            SELECT E, Enom, C, Repr
            FROM   INSERTED ;

    A noter que les triggers pour INSERT et UPDATE peuvent n’en faire qu’un.


    Avec un SGBD authentiquement relationnel (voyez The Third Manifesto), on n’est pas confronté aux problèmes que pose (aujourd’hui) SQL.


    Exemple.

    1) On déclare les variables de relation (au lieu de tables) :

    Variable CLASSE
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    VAR CLASSE BASE RELATION
        {
            C          CHAR
          , Cnom       CHAR
        }
        KEY {C} ;
    Variable ELEVE
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    VAR ELEVE BASE RELATION
        {
            E          CHAR
          , Enom       CHAR
          , C          CHAR
          , Repr       INTEGER
        }
        KEY {E}
        FOREIGN KEY {C} REFERENCES CLASSE ;
    2) On déclare les contraintes que la base de données doit respecter :

    Une classe doit comporter au moins un élève :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    CONSTRAINT CX_01
        CLASSE {C} = ELEVE {C} ;
    Au moins un représentant par classe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    CONSTRAINT CX_02
        MIN (SUMMARIZE ELEVE BY {C} : {S := SUM (Repr)}, S) = 1 ;
    Au plus un représentant par classe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    CONSTRAINT CX_03
        MAX (SUMMARIZE ELEVE BY {C} : {S := SUM (Repr)}, S) = 1 ;

    Pour que ces contraintes soient respectées, on utilise l’affectation multiple, laquelle fait cruellement défaut en SQL où l’on est quelque part obligé de ne pas mettre en œuvre CX_01 et CX_02, sauf à passer (au moins pour CX_01) par des montages lourds : mises à jour par des vues de jointure et REVOKE des mises à jour directes des tables, ou curseurs et autres joyeusetés.

    Affectation multiple :

    Quand on remplace par exemple l’élève e1 par l’élève e2 pour représenter sa classe, on inclut les INSERT qui vont bien dans un même paquet d’instructions (je reprends le style SQL) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    UPDATE ELEVE
        SET Repr = 0 WHERE E  = 'e1' ,  
    
    UPDATE ELEVE
        SET Repr = 1 WHERE E  = 'e2' ;
    La virgule (en rouge dans le code ci-dessus) permet de séparer les instructions dans un paquet et le point-virgule final clôt le paquet. Le SGBD ne déclenche les contrôles (donc les contraintes déclarées ci-dessus) qu’à la détection du point-virgule, avant on a le loisir de faire ce qu'on veut.

    De la même façon, pour respecter la contrainte CX_01, en même temps qu’on crée une classe dans la base de données, dans le même paquet on crée le représentant de la classe (dans le style SQL) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    INSERT INTO CLASSE (C, Cnom) VALUES ('c1', 'classe 1'), 
    INSERT INTO ELEVE (E, Enom, C, Repr) VALUES ('e1', 'Fernand', 'c1', 1) ; 

  11. #11
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Points : 13 092
    Points
    13 092
    Par défaut
    Bonjour...

    ...et merci fsmrel pour ce long discours, accompagné de ses courts schémas.
    C'est toujours un plaisir de vous lire !

    Une petite précision :

    Citation Envoyé par fsmrel Voir le message

    L’embêtant avec SQL est que la structure de l’instruction UPDATE ne permet pas d’affecter en une fois le rôle de représentant d’un élève à un autre, aussi je pense que le moyen les plus simple est de travailler en deux temps. Paer exemple, si l’élève e1 représente sa classe et que désormais c’est au tour de l’élève e2, on aura deux UPDATE :
    On peut en fait permuter les deux élèves en un seul UPDATE, ce qui rend l'opération plus simple :

    Toujours à la mode SQL Server, avec un opérateur bit à bit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    UPDATE ELEVE
        SET Repr = ~Repr 
    WHERE E  IN ('e1' , 'e2' );
    Ou de façon plus générale, avec un CASE :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    UPDATE ELEVE
        SET Repr = CASE Repr
             WHEN 1 THEN 0
             ELSE 1
            END
    WHERE E  IN ('e1' , 'e2' );

  12. #12
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 920
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 21 920
    Points : 51 712
    Points
    51 712
    Billets dans le blog
    6
    Par défaut
    Citation Envoyé par touftouf57 Voir le message
    Merci fsmrel pour votre réponse

    Je me suis probablement mal exprimé. Je ne suis pas contre l'utilisation de trigger....
    Oui, mais le coût d'un déclencheur est beaucoup plus élevé que celui de simples contraintes fussent-elles multiples !

    En effet, le trigger est vérifié APRES la mise à jour des données, alors que les données sont déjà dans la table. Ceci allonge donc la durée de verrouillage exclusif de la table de la durée de traitement du trigger.

    Bref, lorsqu’une solution à base de contraintes est disponible, la préférer dans la plupart des cas !!!!!!!

    A +

  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 119
    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 119
    Points : 31 627
    Points
    31 627
    Billets dans le blog
    16
    Par défaut
    Bien vu Oeil-de-Lynx aieeeuuuuu,

    Je préfère évidemment la 2e solution, car elle ne fait pas appel à l’opération « bitwise logical NOT » (~) que je n’ai pas retrouvée dans la norme SQL (mais peut-être ai-je mal fouillé ? Je m’en remets ès matière à SQLpro).

    Évidemment le trigger INSTEAD OF UPDATE que j’ai proposé est à aménager pour tenir compte de cette possibilité. En tout cas, à touftouf de voir ce qu’il peut faire dans le cas d’un trigger RDB.

    Cela dit, dans le cas très général de la mise en œuvre des contraintes, je continue à appeler de mes vœux l’affectation multiple et la programmation des contraintes dans le style de TTM. Sinon, comment garantir de façon simple, sans échafauder des usines à gaz, des contraintes apparemment anodines telles que — exemple parmi tant d'autres — l’omniprésente cardinalité minimale 1 dans le cas des 1,N fourmillant dans les MCD (cf. contrainte CX_01) ?

  14. #14
    Membre habitué Avatar de touftouf57
    Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2007
    Messages
    362
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2007
    Messages : 362
    Points : 174
    Points
    174
    Par défaut
    Je tiens à tous vous remercier, et plus particulièrement fsmrel, pour vos remarques.

    Je vais plutôt m'orienter vers la solution des contraintes car l'application devra être rapide. Il ne faudrait pas l'handicaper avec des temps de réponse trop longs car notre réseau souffre déjà de ralentissements et les utilisateurs aimeraient que cela aille un peu plus vite.

    En tout cas je vais conserver un lien sur ce post car je l'ai trouvé extrêmement pédagogique.

    Encore un fois : Merci à vous.

  15. #15
    Membre expert
    Avatar de alassanediakite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2006
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Mali

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2006
    Messages : 1 599
    Points : 3 591
    Points
    3 591
    Billets dans le blog
    8
    Par défaut
    Salut à vous
    Je suis tombé sur la discussion en suivant fsmrel!
    Une observation: le fait de lier élève à une classe (clé étrangère idclasse dans table eleve) oblige soit à réinscrire l'élève s'il passe de classe en classe ou à perdre l'historique de ses classes par écrasement de idclasse.
    Alors maitre fsmrel, il n'y a rien à dire
    @+

  16. #16
    Membre habitué Avatar de touftouf57
    Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2007
    Messages
    362
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2007
    Messages : 362
    Points : 174
    Points
    174
    Par défaut
    alassanediakite, le cas élève - classe - représentant est une image de ce que je devais implémenter. Le cas réel aurait été bien trop complexe à exposer. C'est pourquoi j'ai utilisé cette représentation qui est beaucoup plus simple à expliquer.

    Dans mon cas réel, l'historique est conservé dans une autre table qui sera alimentée par trigger ou par code (le choix n'est pas encore arrêté)

  17. #17
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 119
    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 119
    Points : 31 627
    Points
    31 627
    Billets dans le blog
    16
    Par défaut Ne nous dispersons pas...
    Bonsoir,


    Citation Envoyé par alassanediakite Voir le message
    Alors maitre fsmrel, il n'y a rien à dire
    Ben non, Bugs, sinon qu'il ne faut pas déborder du sujet. Le problème soumis par touftouf était précis, il ne fallait surtout pas se disperser, être distraits par la prise en compte d’autres choses comme les historiques, ce sujet (pas toujours simple) étant par ailleurs bien souvent traité dans les forums orientés conception. Si vous voulez un exemple, voyez la discussion « Intranet école supérieure », notamment le cas des élèves qui redoublent.

Discussions similaires

  1. Besoin de conseil SQLite et autre
    Par Jcangel dans le forum Android
    Réponses: 4
    Dernier message: 04/01/2012, 10h49
  2. Convertir une image PNM en autre chose ...
    Par mattmarttigan dans le forum C
    Réponses: 9
    Dernier message: 09/02/2005, 21h34
  3. Réponses: 3
    Dernier message: 24/12/2004, 13h21
  4. Réponses: 1
    Dernier message: 06/01/2003, 08h55
  5. [langage] connaissez-vous autre chose que -d
    Par Sébastien dans le forum Langage
    Réponses: 4
    Dernier message: 05/08/2002, 21h13

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