Précédent Suivant Index

5   Normalisation d'un schéma relationnel

On va maintenant créer des tables, y insérer des informations et effectuer des requêtes sur le schéma obtenu. L'application visée est le système d'information d'un zoo, et on suppose que l'on se trouve dans la situation suivante : une personne peu avertie (elle n'a pas suivi les enseignements du CNAM !) a créé en tout et pour tout une seule table dans laquelle on trouve toutes les informations. Voici le schéma de cette table.

CREATE TABLE Zoo (Animal          Number(4),
                  Nom             VARCHAR2 (20),
                  Annee_naissance NUMBER(4),
                  Espece          VARCHAR2(10),
                  Gardien         VARCHAR2 (20),
                  Prenom          VARCHAR2 (10),
                  Salaire         NUMBER (10,2),
                  Classe          VARCHAR2 (10),
                  Origine         VARCHAR2 (10),
                  Emplacement     NUMBER(4),
                  Surface         NUMBER (3),
                  Type_empl       NUMBER(2),
                  Libelle_empl    VARCHAR2 (20));
Chaque ligne corrrespond à un animal auquel on attribue un nom propre, une année de naissance et une espèce (Ours, Lion, Boa, etc.). Cet animal est pris en charge par un gardien (avec prénom et salaire) et occupe un emplacement dans le zoo (numéro d'emplacement, surface, type_emplacement et libellé_emplacement : savane, désert, forêt, etc.). Enfin chaque espèce appartient à une classe (les mammifères, poissons, reptiles, batraciens ou oiseaux) et on considère pour simplifier qu'elle provient d'une origine unique (Afrique, Europe, etc.).

Vous pouvez consulter avec SQL le contenu de cette table Zoo qui vous est acessible en lecture. On constate à l'oeil nu de nombreuses redondances et anomalies (les voyez-vous ?). Commencez par copier la table chez vous avec la commande suivante :
CREATE TABLE zoo AS
SELECT * FROM zoo;
Le schéma n'est évidemment pas correct : on n'y trouve même pas de contraintes (NOT NULL) ou de définition de clé primaire. Le premier travail est donc de définir un bon schéma relationnel. Pour cela, on vous donne les spécifications suivantes, sous forme de dépendances fonctionnelles : Questions
  1. Le contenu actuel de la table est-il conforme à ces spécifications ? Indication : pour chaque dépendance fonctionnelle A B, où B est un attribut, exécutez l'ordre SQL
    SELECT A, count(DISTINCT B)
    FROM zoo 
    GROUP BY A;
    
    Si la table respecte la DF, count(*) doit valoir ... (à votre avis ?). Vous pouvez donc chercher les anomalies en ajoutant un HAVING COUNT (DISTINCT B) > ... à la requête ci-dessus.


    Solution :

    
    SELECT animal, count (distinct nom), count (distinct annee_naissance), 
            count (distinct espece), count (distinct  emplacement)
    FROM zoo
    GROUP BY  animal
    HAVING count (distinct nom) >1 OR count(distinct annee_naissance) > 1
    OR count(distinct espece) > 1 OR count (distinct emplacement) >1;
    
    SELECT nom, espece, COUNT (distinct animal)
    FROM  zoo
    GROUP BY nom, espece
    HAVING COUNT (distinct animal) > 1;
    
    SELECT espece, count(distinct origine), count(distinct classe)
    FROM zoo
    GROUP BY  espece
    HAVING count (distinct origine)>1 OR count (distinct classe) >1;
    
    SELECT gardien, count(distinct prenom), count(distinct salaire)
    FROM zoo
    GROUP BY  gardien
    HAVING count (distinct prenom)>1 OR count (distinct salaire) >1;
    
    SELECT emplacement,  count(distinct surface), 
            count(distinct type_empl), count (distinct gardien)
    FROM zoo
    GROUP BY  emplacement
    HAVING count (distinct surface)>1 OR count (distinct gardien) >1
    OR     count (distinct type_empl) > 1;
    
    SELECT type_empl,  count(distinct libelle_empl)
    FROM zoo
    GROUP BY  type_empl
    HAVING count (distinct libelle_empl) >1;
    



  2. Effectuez les corrections nécessaires avec des ordres UPDATE. Quand il y a une anomalie ou une incohérence entre deux lignes, on considère que la première est la bonne. IMPORTANT : on valide une mise-à-jour avec la commande commit;.


    Solution :

    Exemple : atribuer un nouveau numéro à l'un des animaux no 2 ; corriger l'ours-batracien en ours-mammifère ; donner un nouveau nom à l'un des deux ours appelés 'Martin', etc. Voici les corrections pour la DF Espece :
    
    UPDATE zoo SET classe = 'Mammifere'
    WHERE    espece = 'Ours';
    
    UPDATE zoo SET  origine = 'Asie' 
    WHERE espece = 'Tigre';
    



  3. Montrer que Animal et Nom, Espèce sont des clés de la table Zoo ?
  4. (trèfle)Montrer que ce sont les seules clés.


    Solution :

    Il est clair que ce sont des clés. Ce sont les seules qui permettent de déduire Nom, donc elles appartiennent à toute clé.


  5. Est-elle en troisième forme normale (donnez un argument formel) ? Y-a-t-il des redondances/anomalies prévisibles ? Les retrouvez-vous dans la table Zoo ?


    Solution :

    On a déjà corrigé les animalies. Pour les redondances, il n'y a pratiquement que ça !


  6. Trouvez un schéma en troisième forme normale, créez les ordres CREATE TABLE correspondant (avec des contraintes PRIMARY KEY, FOREIGN KEY NOT NULL, UNIQUE) et exécutez-les. ATTENTION : une table à laquelle on fait référence dans un FOREIGN KEY doit avoir été créée avant. Il faut donc faire attention à l'ordre de création des tables.


    Solution :

    Les tables sont : ANIMAL, ESPECE, GARDIEN, EMPLACEMENT et TYPE_EMPL. Attention aux contraintes. Voici les commandes :
    
    CREATE TABLE TYPE_EMPL (Type_empl       NUMBER(2) NOT NULL,
                      Libelle_empl    VARCHAR2 (20) NOT NULL,
                      PRIMARY KEY (Type_empl));
    
    CREATE TABLE GARDIEN (Gardien         VARCHAR2 (20) NOT NULL,
                      Prenom          VARCHAR2 (10),
                      Salaire         NUMBER (10,2) NOT NULL,
                      PRIMARY KEY (Gardien));
    
    CREATE TABLE EMPLACEMENT (Emplacement     NUMBER(4),
                      Surface         NUMBER (3),
                      Type_empl       NUMBER(2),
                      Gardien       VARCHAR2(20),
                      PRIMARY KEY (Emplacement),
                      FOREIGN KEY (Type_empl) REFERENCES TYPE_EMPL,
                      FOREIGN KEY (Gardien) REFERENCES GARDIEN);
    
    CREATE TABLE ESPECE (Espece      VARCHAR2(10) NOT NULL,      
                      Classe          VARCHAR2 (10) NOT NULL,
                      Origine         VARCHAR2 (10) NOT NULL,
                      PRIMARY KEY (Espece));
    
    CREATE TABLE ANIMAL (Animal  NUMBER (3) NOT NULL,
                         Nom     VARCHAR2(30) NOT NULL,
                          Annee_Naissance NUMBER(4),
                      Espece          VARCHAR2(10) NOT NULL,
                      Emplacement     NUMBER(4)  NOT NULL,
                    PRIMARY KEY  (Animal),
                    FOREIGN KEY (Espece) REFERENCES ESPECE,
                    FOREIGN KEY (Emplacement) REFERENCES EMPLACEMENT,
                    UNIQUE (Nom, Espece));
    



  7. Une fois le schéma créé, insérez les données dans les tables du 'bon' schéma en les copiant à partir de la table Zoo. Pour copier des données d'une table A vers une table B(B1,B2, ... Bn) , SQL fournit une commande qui est un mélange de SELECT et de INSERT.
             INSERT INTO TABLE B (B1, B2, ... Bn)
             SELECT DISTINCT A1, A2, ... An
             FROM A
             WHERE ...;
          
    En fait l'ordre SELECT peut accéder à plusieurs tables (jointures, différences). Contrainte importante : il doit y avoir autant de Ai que de Bi, et pour les mêmes types. Exemple :
    INSERT INTO ESPECE (espece, classe, origine)
    SELECT DISTINCT espece, classe, origine FROM zoo;
    
Vous devez maintenant avoir un bon schéma et un mauvais (la table Zoo toute seule). Sur ces deux schémas, exprimez les requêtes SQL qui suivent.
  1. Quels sont les Ours du zoo ?
  2. Quels animaux s'appellent Martin ?
  3. Quels animaux habitent dans la jungle ?
  4. De quels animaux s'occupe le gardien Dupond ?
  5. (trèfle)Sur quel(s) emplacement(s) y-a-il des animaux de classes diffrentes (no, surface et libellé du type de l'emplacement).
  6. Somme des salaires des gardiens.
  7. ...

Solution :

Facile, sauf une :

SELECT e.emplacement, e.surface,  t.libelle_empl
FROM   animal a1, animal a2, emplacement e, type_empl t,
           espece es1, espece es2
WHERE     a1.emplacement = a2.emplacement
AND       a1.espece = es1.espece
AND       a2.espece = es2.espece
AND       es1.classe != es2.classe
AND       e.emplacement = a1.emplacement
AND       e.type_empl = t.type_empl;

Considérons les anomalies qui existaient initialement : sont-elles encore possibles dans le 'bon' schéma. D'un autre côté, y-a-t-il des requêtes que l'on peut exprimer sur Zoo et pas sur le nouveau schéma ? (autrement dit : a-t-on perdu de l'information ?)

Conclusion : qu'a-t-on gagné, qu'a-t-on perdu ?


Solution :

On a supprimé la possibilité d'avoir des anomalies, tout en ne perdant pas d'information.





Précédent Suivant Index