Conception de bases de données Conception de bases de données : Exercices de synthèse http://bdd.crzt.fr STÉPHANE CROZAT 30 juillet 2014 Table des matières 3 Objectifs 7 I - Exercice : Zéro Faute 9 II - Exercice : Annuaire 11 III - Exercice : Métiers 13 IV - Base de données et programmation 15 V - Expertises 19 A. Mise en situation.........................................................................................19 B. Étude relationnel : Rétro-conception UML.......................................................20 C. Exercice : Étude relationnel : Dépendances fonctionnelles................................20 D. Exercice : Étude relationnel : Forme normale.................................................21 E. Exercice : Étude relationnel : Normalisation....................................................21 F. Étude d'exploitation : Implémentation SQL.....................................................21 G. Étude d'exploitation : Surveillance des réserves..............................................22 H. Étude d'exploitation : Comptes « lourds »......................................................22 I. Étude d'exploitation : Transferts....................................................................22 J. Exercice : Étude d'exploitation : Vigilance.......................................................22 K. Exercice : Étude technologique : Fonction Access............................................23 L. Exercice : Étude technologique : Trigger Oracle...............................................23 M. Exercice : Étude technologique : LAPP...........................................................24 N. Étude Relationnel-Objet...............................................................................25 O. Étude attributs multi-valués.........................................................................25 VI - Étudiants et UVs 27 A. Exercice.....................................................................................................27 B. Exercice.....................................................................................................28 C. Exercice.....................................................................................................29 VII - Gestion de comptes bancaires 31 A. Récupération Access....................................................................................31 B. Migration Oracle..........................................................................................33 VIII - Super-héros 4 35 A. Super-héros relationnels..............................................................................35 B. Super-héros relationnels-objets....................................................................36 C. Super-héros sans tête.................................................................................37 D. Super-lents................................................................................................38 E. Super-transferts..........................................................................................39 IX - Friends 41 A. Exercice.....................................................................................................41 B. Exercice.....................................................................................................43 X - E.T. 45 A. Exercice.....................................................................................................45 B. Exercice.....................................................................................................47 XI - Attributs multi-valués 49 A. Documentum..............................................................................................49 B. Fichier CSV.................................................................................................50 C. Insertion de collections à partir de requêtes SELECT........................................52 D. Attributs multi-valués et relationnel..............................................................53 E. Attributs multi-valués et relationnel-objet......................................................55 Solution des exercices 57 Solution des exercices 93 Glossaire 99 Signification des abréviations 101 Index 103 5 Objectifs Ce module permet de réviser par des exercices d'application l'ensemble des concepts et technologies étudiés dans le cadre de la formation à la conception de bases de données (correspondant à l'unité de valeur NF17 à l'UTC). 7 Exercice : Zéro Faute I I- [40 min] L'entreprise ZéroDéfo veut répertorier ses fautes de production. Une faute est définie dans le manuel qualité de l'entreprise comme « un défaut constaté sur une pièce produite en fin de chaîne ». L'entreprise veut associer les fautes aux produits concernés. Chaque faute est classifiée dans des catégories et sous-catégories. Chaque produit est basé sur un modèle. Pour chaque modèle, on veut gérer son code constitué de 8 caractères alphanumériques, son nom et la date de mise sur le marché. Pour chaque produit, on veut connaître le modèle associé, le numéro de série (6 chiffres) et le numéro de produit (max. 4 caractères) ainsi que l'année de production. Un produit est identifié par son numéro de série et son numéro de produit. Plusieurs produits partagent le même numéro de série (tous les produits de cette série), et deux produits peuvent avoir (par hasard) le même numéro de produit, dans des séries différentes (qui ont adopté le même système de codage des produits). Une faute concerne toujours un produit. Elle possède un code unique, un titre et la date de détection. Elle peut éventuellement avoir un commentaire et la date de réparation si le produit a été réparé. Les fautes sont classifiées dans des sous-catégories et chaque souscatégories fait partie d'une catégorie. Une faute est toujours classifiée dans une catégorie au moins (elle peut être classifiée dans plusieurs). Les catégories et les sous-catégories possèdent un nom et, optionnellement, une description. Question 1 [Solution n°1 p 57] Proposez un diagramme UML répondant aux besoins de cette entreprise. Question 2 [Solution n°2 p 57] Proposez un modèle relationnel en 3NF basé sur votre modèle UML. Question 3 [Solution n°3 p 58] Écrivez le code SQL LDD permettant de créer la base de données correspondant au modèle relationnel. Question 4 [Solution n°4 p 58] Écrivez une requête SQL permettant de lister le nombre de fautes par nom de modèle et par numéro de série. 9 Exercice : Zéro Faute 10 Exercice : Annuaire II II - [30 minutes] Soit le modèle relationnel suivant représentant l'annuaire de l'UTC : 1 personne (nom, prenom, localisation, departement, unite) Soit un extrait de fichier CSV issu de la base de données (Un fichier CSV, Coma Separated Value, est un enregistrement d'une table dans un fichier texte). 1 2 3 4 5 Dupont;Stéphane;PG M 290;TSH;ICS Dupuis;Katie;PG J 178;TSH;ICS Durand;Paul;CR C 276;GI;Heudiasyc Dusse;Jean-Claude;CR A 320;GI;Heudiasyc Dutour;Jean;CR C 198;GI;LMAC Question 1 [Solution n°5 p 59] En quelle(s) forme(s) normale(s) est ce modèle ? Question 2 [Solution n°6 p 59] Écrivez l'opération d'algèbre relationnelle permettant de renvoyer les noms des personnes situées dans le même bureau. Question 3 [Solution n°7 p 59] Écrivez une transaction permettant de copier tous les enregistrements dans une table de sauvegarde personne_backup de même schéma que personne, juste après avoir vidé personne_backup. En cas de panne pendant la transaction, le système doit revenir à son état initial, c'est à dire que personne_backup n'a été ni vidé ni rempli des données de "personne". Question 4 [Solution n°8 p 59] Implémentez une solution d'optimisation permettant d'exécution suivante (en dehors de la vue concrète) : 1 2 d'améliorer la vitesse SELECT * FROM Personne ORDER BY Nom, Prenom Question 5 [Solution n°9 p 59] Écrivez un déclencheur (syntaxe Oracle) permettant d'assurer que les chaînes du champ "departement" seront toujours en majuscule (Utiliser la fonction UPPER). 11 Exercice : Métiers 12 III - Exercice : Métiers III Soit une base de données composée de deux tables tPersonne et tMetier. 1 2 tPersonne (#numero, nom, prenom, metier=>tMetier) tMetier (#code, intitule) On souhaite écrire 6 programmes qui permettent d'accéder à BD afin d'afficher la liste des noms et des métiers sur la sortie standard. Question 1 [Solution n°10 p 59] Écrivez un programme PHP implémentée sous MySQL. Résultat attendu en HTML : 1 2 3 4 5 6 permettant d'interroger la base de données <html> <p>Dupont Ingénieur</p> <p>Durand Architecte</p> <p>Dupuis Secrétaire</p> ... </html> Question 2 [Solution n°11 p 60] Écrivez un programme PHP permettant d'interroger la implémentée sous PosgreSQL (et d'obtenir le même résultat). base de données Question 3 [Solution n°12 p 60] Écrivez un programme PHP permettant d'interroger implémentée sous Oracle (et d'obtenir le même résultat). la base de données Question 4 [Solution n°13 p 60] Écrivez un programme VBA permettant d'interroger la base de données implémentée sous Access. Résultat attendu sur la sortie standard (utilisez Debug.Print et l'opérateur de concaténation &) : 1 2 3 4 Dupont Ingénieur Durand Architecte Dupuis Secrétaire ... 13 Exercice : Métiers Question 5 [Solution n°14 p 60] Écrivez un programme PL/SQL permettant d'interroger la base de données implémentée sous Oracle. Résultat attendu sur la sortie standard (utilisez DBMS_OUTPUT.PUT_LINE et l'opérateur de concaténation ||) : 1 2 Dupont Ingénieur ... Question 6 [Solution n°15 p 61] Écrivez un programme Java permettant d'interroger la base de données implémentée sous Oracle. Résultat attendu sur la sortie standard (utilisez System.out.println et l'opérateur de concaténation +) : 1 2 14 Dupont Ingénieur ... Base de données et programmation IV IV - Soit une base de données composée d'une unique table T, contenant un unique attribut A. 1 T(#A:string) On souhaite écrire 6 programmes qui permettent d'accéder à la base de données, afin d'afficher la liste des valeurs de A. Exercice 1 : PHP & PosgreSQL [Solution n°1 p 93] Écrivez un programme PHP implémentée sous PosgreSQL. <?php permettant d'interroger la base de données $conn=pg_connect($host,$user,$passwd,$bdd); $query = " "; $result=pg_query( , ); echo "<html>"; while($row = pg_fetch_row( )) { echo "<p> </p>"; } echo "</html>"; pg_close($conn); ?> Exercice 2 : PHP & MySQL [Solution n°2 p 93] Écrivez un programme PHP implémentée sous MySQL. <?php permettant d'interroger la base de données $conn=mysql_connect($host,$user,$passwd); mysql_select_db($bdd, $conn); $query = " "; $result=mysql_query( , ); echo "<html>"; 15 Base de données et programmation while($row = mysql_fetch_row( )) { echo "<p> </p>"; } echo "</html>"; mysql_close($conn); ?> Exercice 3 : PHP & Oracle [Solution n°3 p 93] Écrivez un programme PHP implémentée sous Oracle. <?php permettant d'interroger la base de données //définition des variables de connexion à ajouter $conn=oci_connect($user, $passwd, $bd)) $query = " "; $statement = oci_parse( , ); oci_execute($statement); echo "<html>"; while($row = oci_fetch_array( )) { echo "<p> </p>"; } echo "</html>"; oci_close($conn); ?> Exercice 4 : Access & VBA [Solution n°4 p 94] Écrivez un programme VBA implémentée sous Access. Sub printA() vSql = " permettant For i = 1 To vRs.RecordCount vRs.MoveNext Next i End Sub 16 la base " Set vRs = CurrentDb.CreateQueryDef("", Debug.Print d'interroger ! ).OpenRecordset de données Base de données et programmation Exercice 5 : Oracle & PL/SQL [Solution n°5 p 94] Écrivez un programme PL/SQL permettant d'interroger la base de données implémentée sous Oracle. SET SERVEROUTPUT ON; DECLARE CURSOR cT IS vA ; %TYPE; BEGIN OPEN ; LOOP FETCH INTO ; EXIT WHEN cT%NOTFOUND; DBMS_OUTPUT.PUT_LINE( ); END LOOP; END; Exercice 6 : Oracle & Java [Solution n°6 p 94] Écrivez un programme Java implémentée sous Oracle. package nf17; permettant d'interroger la base de données import java.sql.*; import oracle.jdbc.OracleDriver; public class Exercice { public static void main(String[] args) { try { DriverManager.registerDriver (new OracleDriver()); Connection vCon = DriverManager.getConnection("jdbc:oracle:thin:nf17/nf17@localhost:1521:test"); Statement vSt = vCon.createStatement(); String vSql = " "; ResultSet vRs = vSt.executeQuery( ); while(vRs.next()) { System.out.println( .getString(1)); } } catch (Exception e) { e.printStackTrace(); } } 17 Base de données et programmation } 18 V- Expertises V Mise en situation 19 Étude relationnel : Rétro-conception UML 20 Exercice : Étude relationnel : Dépendances fonctionnelles 20 Exercice : Étude relationnel : Forme normale 21 Exercice : Étude relationnel : Normalisation 21 Étude d'exploitation : Implémentation SQL 22 Étude d'exploitation : Surveillance des réserves 22 Étude d'exploitation : Comptes « lourds » 22 Étude d'exploitation : Transferts 23 Exercice : Étude d'exploitation : Vigilance 23 Exercice : Étude technologique : Fonction Access 23 Exercice : Étude technologique : Trigger Oracle 24 Exercice : Étude technologique : LAPP 24 Étude Relationnel-Objet 25 Étude attributs multi-valués 26 [2 heures] A. Mise en situation Vous avez en charge une étude pour la réalisation d'une base de données de gestion pour une association. Vous disposez pour seule donnée d'entrée du modèle relationnel MR ci-dessous, réalisé par un ingénieur qui vient de partir pour une retraite de 6 ans au Népal. 1 2 3 4 Compte (#num:entier, categorie:{A|R}, =solde():reel) Recette(#num=>Compte, #date:date, montant:reel, annee:entier) Depense(#num=>Compte, #date:date, montant:reel, annee:entier) Les comptes A ou « actifs » sont les comptes courants de l'association. Les comptes R ou « réserve » sont des comptes sur lesquelles les opérations sont rares et n'interviennent qu'en cas de problème de trésorerie ou d'investissement important. La notation « =solde():reel » n'est pas usuelle en relationnel. Elle signifie qu'une 19 Expertises méthode « solde » renvoyant un réel est associée à la relation. Bien entendu cette méthode ne sera pas implémentée en SQL LDD et devra être programmée à part, dans une procédure stockée par exemple. Vous allez réaliser une double étude en relationnel et en relationnel-objet, et tester différentes technologies. B. Étude relationnel : Rétro-conception UML Afin de mener sereinement votre étude, vous décidez de réaliser le modèle conceptuel MC qui a servi à réaliser le modèle relationnel MR dont vous disposez. Question [Solution n°16 p 61] Réalisez le schéma UML permettant d'arriver à MR. Vous chercherez le meilleur schéma UML possible. C. Exercice : Étude relationnel : Dépendances fonctionnelles [Solution n°7 p 95] Analysez la sémantique du schéma MR et sélectionnez, parmi les propositions suivantes, toutes les dépendances fonctionnelles ayant du sens (on note C, R et D les relations Compte, Recette et Depense de MR). C.num→C.categorie C.categorie→C.num (R.num, R.date)→R.montant, R.annee R.num→R.date R.annee→R.date R.date→R.annee (D.num, D.date)→D.montant, D.annee D.num→D.date D.annee→D.date D.date→D.annee 20 Expertises D. Exercice : Étude relationnel : Forme normale [Solution n°8 p 95] Il est évident que ce schéma n'est pas en 3NF, mais est-il en 2NF ? Justifiez. NB : L'on considérera qu'une date est bien atomique. Oui, le schéma est en 2NF Non, le schéma est seulement en 1NF E. Exercice : Étude relationnel : Normalisation [Solution n°9 p 96] Parmi les propositions MR' suivantes, sélectionnez toutes celles qui permettent de parvenir à un schéma en 3NF, sans perte d'information. Expliquez et critiquez chaque solution choisie. Si vous avez choisi plusieurs solutions, classez ces solutions entre elles de la meilleure à la moins bonne. Compte (num:entier, categorie:{A|R}, =solde():reel) Recette(num=>Compte, date=>AnneeR, montant:reel) AnneeR(date:date, annee:entier) Depense(num=>Compte, date=>AnneeD, montant:reel) AnneeD(date:date, annee:entier) Compte (num:entier, categorie:{A|R}, =solde():reel) Recette(num=>Compte, date=>Annee, montant:reel) Depense(num=>Compte, date=>Annee, montant:reel) Annee(date:date, annee:entier) Compte (num:entier, categorie:{A|R}, =solde():reel) Recette(num=>Compte, date:date, montant:reel) Depense(num=>Compte, date:date, montant:reel) F. Étude d'exploitation : Implémentation SQL Vous décidez d'ignorer le schéma MR' et de poursuivre avec le schéma relationnel MR (vous resterez donc avec un schéma redondant). Vous avez à disposition les types suivants INTEGER(X), DECIMAL(X,Y), CHAR(X) et VARCHAR(X), ainsi que DATE et DATETIME. NB : On pose qu'il existera toujours moins de 100 comptes pour l'association. Question [Solution n°17 p 62] Implémentez en SQL standard le schéma MR non normalisé (les types, ainsi que X et Y devront être bien choisis). 21 Expertises G. Étude d'exploitation : Surveillance des réserves Réalisez une simulation de recherche d'opérations sur les comptes de réserve R. Question [Solution n°18 p 62] Écrivez en algèbre relationnelle la suite d'opérations permettant de trouver les numéros des comptes de catégorie R tels qu'ils ont connu au moins une dépense. H. Étude d'exploitation : Comptes « lourds » Vous cherchez à implémenter la question permettant de trouver tous les comptes « lourds », c'est à dire ayant de très fortes recettes. Question [Solution n°19 p 62] Écrivez en SQL une requête permettant d'afficher la liste des numéros des comptes dont les recettes sont supérieures à 1.000.000 €, depuis leur création. I. Étude d'exploitation : Transferts Etudiez à présent les modalités d'un transfert de compte à compte. Question [Solution n°20 p 62] Ecrivez une transaction permettant de transférer 100€ du compte n°125 au compte n°222 le 20 juin 2006 à 12h02. Rappel : Les insertions de date se feront selon la syntaxe standard : 'YYYY/MM/DD HH:MI' J. Exercice : Étude d'exploitation : Vigilance [Solution n°10 p 96] Le trésorier a besoin de vérifier chaque matin en arrivant, parmi les comptes A de l'année en cours, lesquels sont très actifs : c'est à dire combien d'opérations de dépenses sont effectuées sur chaque compte. Sélectionnez la meilleure solution d'optimisation parmi celles proposées ci-dessous pour cette requête. Expliquez. 22 Expertises Indexation Partitionnement horizontal Partitionnement vertical Dénormalisation Vue matérialisée K. Exercice : Étude technologique : Fonction Access [Solution n°11 p 96] Complétez le code VBA ci dessous afin qu'il permettent d'impémenter la méthode « solde() », qui fait la différence entre les recettes et les dépenses pour un compte donné. Expliquez pourquoi, sous Access, les méthodes sont implémentées comme des fonctions VBA indépendantes des tables ? Rappel : Le caractère de concaténation de chaîne sous Access est "&" : "A" & "B" renvoie "AB". Function solde(pNum As Integer) As Real Dim vCodeSql As String Dim vR As Real Dim vD As Real vCodeSql = vR = CurrentDb.CreateQueryDef("", vCodeSql).OpenRecordset.Fields(0) vCodeSql = vD = CurrentDb.CreateQueryDef("", vCodeSql).OpenRecordset.Fields(0) solde= End Function L. Exercice : Étude technologique : Trigger Oracle [Solution n°12 p 97] Complétez le code PL/SQL ci-dessous afin qu'il implémente un trigger permettant de systématiquement recalculer la valeur du champs « annee », en fonction du champs « date » à chaque insertion et mise à jour des données. Expliquez en quoi c'est une solution au choix précédent qui avait été fait de ne pas normaliser le schéma. Rappel : La fonction SQL "YEAR" renvoie l'année d'une date sous la forme d'un entier. CREATE TRIGGER RecetteTrig BEFORE INSERT OR UPDATE 23 Expertises ON Recette FOR EACH ROW BEGIN END ; M. Exercice : Étude technologique : LAPP [Solution n°13 p 97] Soit la page HTML suivante : 1 2 3 4 5 6 7 8 9 <html xmlns="http://www.w3.org/1999/xhtml"> <body> <form method="GET" action="insert.php"> Numéro : <input type="text" name="n"/> Catégorie : <input type="text" name="c"/> <input type="submit"/> </form> </body> </html> Complétez le code ci-dessous du fichier "insert.php" qui sera appelé pour insérer un nouveau compte dans une BD PostgreSQL. <html xmlns="http://www.w3.org/1999/xhtml"> <body> <?php /* Initialisation */ $vserveur="monassos.free.fr" ; $vport="5432" ; $vcu="president" ; $vbase="bdassos" ; $vcp="bonjour" ; /* Connexion à la BD */ $c = user= pg_connect("host= password= port= dbname= "); /* Exécution de la requête*/ $sql = "INSERT INTO Compte (". .",'". ."')" ; ; /* Finalisation */ pg_close($c); echo "<p>Insertion du compte numéro". ?> </body> </html> 24 ."effectuée</p>" ; Expertises N. Étude Relationnel-Objet Vous prolongez présent votre travail par une étude du Relationnel-Objet et de son implémentation en SQL3 sous Oracle. NB : Nous considérerons à présent l'année, dans les relations Recette et Dépense, comme une méthode et non plus comme un attribut. Question 1 [Solution n°21 p 62] Réalisez le modèle MRO, équivalent relationnel-objet du modèle MR. Le modèle MRO ne devra contenir qu'une seule table. Bien entendu vous créerez autant de types et de collections (donc de tables imbriquées) que vous le souhaitez. Question 2 [Solution n°22 p 63] Déclarez vos types ainsi que votre table, sans implémenter les méthodes (corps de type). Question 3 [Solution n°23 p 63] Implémentez la méthode annee() dans le corps de type correspondant. Rappel : La fonction PL/SQL "YEAR" renvoie l'année d'une date sous la forme d'un entier. Question 4 [Solution n°24 p 63] Implémentez la méthode solde() dans le corps de type correspondant. Question 5 [Solution n°25 p 63] Créez le compte numéro 1, de catégorie A dans votre table, avec une recette de 100 € le 20 juillet 2007 à 11h31. Rappel : utilisez la fonction Oracle TO_DATE pour insérer des dates sous Oracle Question 6 [Solution n°26 p 64] Afin de testez sommairement votre implémentation, écrivez la requête permettant d'afficher tous les numéros de compte avec tous leurs soldes. O. Étude attributs multi-valués Des propriétés complémentaires sont ajoutées à l'entité Compte, sous la forme de deux attributs multivalués : signatures (qui contient la liste des noms ayant signature sur le compte) et procurations (qui contient la liste des noms ayant procuration sur le compte). Pour cette dernière étude le schéma étudié sera uniquement le schéma conceptuel simplifié MC2 suivant. 25 Expertises Compte num:entier categorie:{A|R} signatures[1..n]:chaîne procurations[1..n]:chaîne Graphique 1 Compte Question 1 [Solution n°27 p 64] Donnez la transcription de cette classe en relationnel standard (MR2rc), en relationnel-objet sous Oracle (MR2ro) et en relationnel sous Documentum (MR2rd) Question 2 [Solution n°28 p 64] Donnez l'avantage et l'inconvénient de la méthode relationnel Documentum par rapport au relationnel classique. 26 VI - Étudiants et UVs VI Exercice 29 Exercice 30 Exercice 32 A. Exercice On dispose du schéma UML ci-après qui décrit des étudiants, des UV, les notes obtenues par les étudiants à ces UV, et les diplômes d'origine de ces étudiants. Étudiants et UVs {key} désigne des clés candidates, ici toutes les clés ne sont composées que d'un seul attribut {local key} désigne une clé locale un semestre est de la forme 'PYYYY' ou 'AYYYY' (où YYYY désigné une année sur 4 chiffre) ; exemple : 'A2013', 'P2014... Rappel : Notion de clé locale dans classes d'association (cf. Transformation des classes d'association) Question 1 [Solution n°29 p 64] Traduire le schéma en modèle logique relationnel (MLD1). On choisira obligatoirement les clés primaires parmi celles nécessitant le plus petit nombre de bits possible pour leur codage. Question 2 [Solution n°30 p 64] Écrire en SQL la vue vmax1 permettant de renvoyer pour chaque titre d'UV, intitulé de diplôme et semestre, la note maximale obtenue par un étudiant. uv Bases de données diplome Deutech UTC semestre P2014 max 18 27 Étudiants et UVs uv diplome semestre max Bases de données Deutech UTC A2013 17 Bases de données DUT Budapest P2014 15 Bases de données DUT Budapest A2013 16 ... Tableau 1 Exemple de résultat retourné par la vue vmax1 Question 3 [Solution n°31 p 64] Montrer qu'un choix différent pour les clés primaires aurait permis d'optimiser cette vue. Proposer le modèle logique relationnel correspondant (MLD2) et la nouvelle vue vmax2 associée. Question 4 [Solution n°32 p 65] Proposer un programme PHP permettant d'afficher en HTML les informations de la vue vmax1 ou vmax2 (au choix) restreintes à l'année 2014. On pourra utiliser la fonction fConnect() qui renvoie un identifiant de connexion. On travaillera avec une base PostgreSQL. B. Exercice On rappelle que la présence de redondance dans un modèle relationnel doit être documentée par la mise en évidence des DF responsables. Question 1 [Solution n°33 p 65] Les performance de la page PHP proposée ne sont pas satisfaisantes. La cause du problème est identifiée comme étant la jointure qui s'exerce entre les tables etudiant et note. Proposer une solution de dénormalisation. Proposer le modèle logique relationnel correspondant (MLD3) et la nouvelle vue vmax3 associée. Afin de contrôler la redondance introduite par la dénormalisation, on décide de créer : une table etucontrole qui contiendra tous les numéros d'étudiants dont les informations ne sont pas cohérentes (dont le nom ou le diplôme possède plusieurs valeurs différentes) : etucontrole(#num=>etudiant) ; un trigger tretudiant qui viendra ajouter les numéros incohérents à chaque mise à jour ou insertion dans la base de données. Question 2 [Solution n°34 p 65] Écrire le trigger tretudiant. Ce trigger testera l’existence préalable de l'étudiant inséré ou mis à jour et en cas d'incohérence insérera le numéro dans la table etucontrole. On utilisera la syntaxe Oracle. Indice : 28 Étudiants et UVs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 CREATE TRIGGER tretudiant BEFORE INSERT OR UPDATE ON etudiant FOR EACH ROW DECLARE vnum etudiant.num%TYPE; vnom etudiant.nom%TYPE; vorigine etudiant.origine%TYPE; BEGIN /** On récupère les informations éventuellement déjà existantes concernant l'étudiant inséré ou mis à jour ; on utilise la fonction max pour ne retourner qu'un seul tuple **/ /** On teste que ces valeurs sont les mêmes que celles nouvellement insérées ou mises à jour et sinon on insère dans la table 'etucontrole' **/ END; Question 3 [Solution n°35 p 66] Écrire en SQL la création de la table etucontrole ainsi que la requête permettant d'obtenir la liste des incohérences. 1 Turing TC UTC 1 Turingue TC UTT 3 von Neumann DUT Budapest 3 Vonneumann DUT Budapest 3 von Neumann DUT Budpapest Tableau 2 Exemple d'incohérences Question 4 [Solution n°36 p 66] Écrire la séquence SQL permettant de corriger les informations concernant les étudiants '1' et '3'. On considérera ici que les lignes correctes sont : 1 Turing TC UTC 3 von Neumann DUT Budapest Préciser pourquoi il est nécessaire de modifier plusieurs tables. Préciser pourquoi il est nécessaire d'utiliser des transactions. Préciser pourquoi l'ordre de certaines opérations a de l'importance. C. Exercice Question [Solution n°37 p 67] Traduire le modèle UML Étudiants et UVs en relationnel-objet en utilisant la syntaxe SQL3 définie sous Oracle. Utiliser impérativement le modèle table-objet et 29 Étudiants et UVs les références par OID. Utiliser le modèle imbriqué lorsque c'est pertinent. 30 Gestion de comptes bancaires VII - VII Récupération Access 33 Migration Oracle 35 A. Récupération Access [30 minutes] Nouvel employé d'une entreprise bancaire, vous avez pour mission de restructurer une base de données existante pour la gestion des opérations bancaires (débits et crédits sur un compte). La base de données existante a été conçue sous MS Access, avec une unique table. Image 1 Schéma de la seule table de la BD NB : Un débit est différencié d'un crédit par la valeur de MontantOperation : Si elle est négative il s'agit d'un débit, si elle est positive il s'agit d'un crédit. En vue de préparer la re-conception de cette base, un stagiaire a effectué le recensement des DF de ce schéma. Voici le résultat de ses travaux, sous la forme de la fermeture transitive des DF : NumClient→Nom, Prenom NumCompte→NumClient, Nom, Prenom, Agence, Ville, Pays, Monnaie Agence→Ville, Pays, Monnaie Pays→Monnaie 31 Gestion de comptes bancaires DateOperation, NumCompte→MontantOperation Question 1 [Solution n°38 p 72] Trouvez toutes les clés candidates pour ce schéma, puis choisissez une clé primaire. Justifier. Question 2 [Solution n°39 p 72] En quelle forme normale est le schéma de la base existante ? Justifier. Question 3 [Solution n°40 p 72] Afin de préparer la décomposition du schéma relationnel, trouvez une couverture minimale des DFE pertinente. Question 4 [Solution n°41 p 72] Décomposer le schéma relationnel de la base existante, en préservant les DF, pour aboutir à un schéma en 3NF. Choisissez la meilleure clé primaire pour chaque relation. Présentez le schéma relationnel résultant et justifiez. Question 5 [Solution n°42 p 72] A des fins de documentation, retro-concevez le MCD qui aurait permis d'aboutir directement à ce schéma relationnel. Question 6 [Solution n°43 p 72] L'équipe technique ne connaissant pas Access, elle fait appel à vous pour traduire les requêtes qui existaient dans l'ancien système. On s'attache en particulier à la requête QBE suivante : Image 2 Requête RTotalParPersonneEtParMonnaie 32 Gestion de comptes bancaires Vous avez accès à son équivalent SQL grâce au générateur automatique de SQL de Access : 1 2 3 SELECT TBank.NumClient, TBank.Monnaie, Sum(TBank.MontantOperation) AS SommeDeMontantOperation FROM TBank GROUP BY TBank.NumClient, TBank.Monnaie; En SQL (standard), créez une vue équivalente à cette requête QBE, fonctionnant avec le nouveau schéma. B. Migration Oracle [30 minutes] Récupération des données sous Oracle Une équipe technique est désignée afin de réaliser la nouvelle base de données que vous avez préconisée sous Oracle. Une fois le schéma relationnel créé, elle fait appel à vous pour récupérer les données existantes. Question 1 [Solution n°44 p 73] Écrivez une série d'instructions SQL permettant la récupération des données depuis l'ancienne base de données vers la nouvelle. Amélioration de l'application L'équipe technique fait à présent appel à vous pour certains aspects particuliers de l'application. Notez la table suivante qui a été ajoutée au schéma relationnel : 1 CREATE TABLE TDeficit (NumCompte number(10)); Question 2 [Solution n°45 p 73] Écrivez un trigger qui, lorsque l'on insère une opération de débit qui conduit à un solde négatif du compte, insère le numéro du compte dans la table "TDeficit". Question 3 [Solution n°46 p 74] Écrivez une méthode booléenne Java qui prend en paramètre d'entrée un numéro de client et renvoie vrai si ce client a déjà été en déficit. On notera que la base se nomme "bank", qu'elle est accédée par la méthode sur la machine "www.oracle.bank.com" (port "1521"), par l'utilisateur "Java" (mot de passe "Tango"). Fiabilisation L'équipe informatique fait maintenant appel à vous pour fiabiliser la base de données, à présent utilisée. Un problème récurrent survient sur la procédure PL/SQL "pVirement" donnée ci-dessous. En effet suite à des micro-coupures réseau, il arrive que le débit du "Compte1" soit effectué, tandis que le crédit du "Compte2" n'est jamais effectué. 1 2 3 4 CREATE procedure pVirement (Compte1 number, Compte2 number, Montant number, DateOp date) IS BEGIN INSERT INTO TOperation VALUES (Compte1, DateOp, Montant * -1); 33 Gestion de comptes bancaires 5 6 INSERT INTO TOperation VALUES (Compte2, DateOp, Montant); END; Question 4 [Solution n°47 p 74] Expliquez pourquoi le problème survient. Proposer une solution générale au problème. Implémentez cette solution en réécrivant la procédure PL/SQL. Optimisation L'équipe informatique fait enfin appel à vous pour optimiser la base, et en particulier la requête suivante qui, étant donné un nombre d'opérations très grand existant, est trop longue à s'exécuter. 1 2 3 4 SELECT NumCompte, MontantOperation FROM TOperation WHERE MontantOperation > 10000; ORDER BY MontantOperation; Question 5 [Solution n°48 p 74] Expliquez pourquoi cette requête sera longue à s'exécuter si le nombre d'enregistrements est très grand dans la table TOperation. Proposer une solution générale au problème. Proposer une définition SQL implémentant cette solution pour ce cas particulier. 34 VIII - Super-héros VIII Super-héros relationnels 39 Super-héros relationnels-objets 40 Super-héros sans tête 41 Super-lents 43 Super-transferts 44 A. Super-héros relationnels [30 min] La gamme de super-héros GARVEL veut réaliser la base de données de leurs figurines articulées. La société a fait réaliser un modèle UML qui doit servir de point de départ à la mise en œuvre. Modèle UML Figurines GARVEL Question 1 [Solution n°49 p 74] Transformer le modèle UML en modèle relationnel (justifier les passages non triviaux, en particulier la relation d'héritage). 35 Super-héros Question 2 [Solution n°50 p 75] Donner, en algèbre relationnel, la liste de tous les personnages qui sont basés au repaire 'La Ratcave'. Question 3 [Solution n°51 p 75] Donner, en algèbre relationnel, la couleur des torses des personnages habitant la 'GARVEL Tower', pilotant des véhicules aquatiques et ayant comme mentor Superman. Question 4 [Solution n°52 p 75] Donner, en algèbre relationnel, la liste des désignations de toutes les figurines ayant un prix inférieur à 10 (une seule colonne désignation sera projetée). B. Super-héros relationnels-objets [30 minutes] Modèle UML Figurines GARVEL Question 1 [Solution n°53 p 76] Transformer le modèle UML en modèle relationnel-objet. On utilisera les OID et le nested model. Question 2 [Solution n°54 p 77] Donner, en SQL, la liste de tous les personnages qui sont basés au repaire "La Ratcave". Question 3 [Solution n°55 p 77] Donner, en SQL, la couleur des torses des personnages habitant la "GARVEL Tower", pilotant des véhicules aquatiques et ayant comme mentor Superman. 36 Super-héros C. Super-héros sans tête [10 minutes] Les usines GARVEL construisent des figurines de super-héros à partir des données présentes dans la base de données PostgreSQL de l'entreprise. Un gros problème est survenu le mois dernier, lorsque l'usine en charge d'une nouvelle figurine, Superchild, a livré un million d'exemplaires sans tête. À l'analyse de la base il a en effet été observé que la base contenait un tuple "Superchild" dans la table Personnage, et cinq tuples associés dans la table Membre, deux pour les bras, deux pour les jambes et un pour le torse, mais aucun pour la tête. Le service qui a opéré la saisie du nouveau personnage assure, sans ambiguïté possible, que la tête a pourtant été saisie dans la base. En revanche, l'enquête montre des instabilités de son réseau à cette période. L'extrait du modèle UML utile au problème est proposé ci-après, ainsi que le code SQL exécuté via le client psql lors de l'insertion de la nouvelle figurine. Modèle UML Figurines GARVEL (extrait) 1 2 3 4 5 6 7 8 \set AUTOCOMMIT on INSERT INTO Personnage (designation, prix, identite_secrete, genre) VALUES ('Superchild','12','Jordy','superhéros') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','bras droit','bleu') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','bras gauche','bleu') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','jambe gauche','bleu') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','jambe droite','bleu') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','torse','rouge') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','tete','rose') ; Question 1 [Solution n°56 p 77] Expliquer la nature du problème qui est probablement survenu. Proposer une solution générale pour que le problème ne se renouvelle pas, en expliquant 37 Super-héros pourquoi. Soyez concis et précis : La bonne mobilisation des concepts du domaine et la clarté de la rédaction seront appréciées. Question 2 [Solution n°57 p 77] Illustrer la solution proposée en corrigeant le code SQL de l'insertion de "Superchild". D. Super-lents [10 minutes] L'entreprise GARVEL propose des figurines de super-héros à acheter en ligne sur un site Internet adossé à sa base de données. Son catalogue a atteint cette année le million de modèles. Depuis quelques temps, et la récente forte augmentation des modèles au catalogue, les performances des consultations ont beaucoup chuté, entraînant des temps d'accès lents (plusieurs secondes) et une baisse des actes d'achat. La requête la plus utilisée par l'application Web sert à lister tous les super-héros avec toutes leurs caractéristiques, avec leurs véhicules et leurs repaires (et également toutes leurs caractéristiques) et à la trier par ordre de prix. Modèle UML Figurines GARVEL Soyez concis et précis dans vos réponses ; La bonne mobilisation des concepts du domaine et la clarté de la rédaction seront appréciées. Question 1 [Solution n°58 p 78] Expliquer pourquoi cette requête peut poser des problèmes de performance, lorsque la base comprend de nombreux enregistrements. Question 2 [Solution n°59 p 78] Proposer et justifier une première solution d'optimisation à mettre en œuvre qui sera utile dans tous les cas et n'aura que peu d'effets indésirables. Puis, expliquer pourquoi le passage en relationnel-objet de la base de données, combiné à la solution précédente, peut améliorer considérablement les performances. 38 Super-héros Question 3 [Solution n°60 p 78] Proposer deux solutions alternatives qui, si l'on reste en relationnel, permettraient d'améliorer considérablement les performances. Vous en poserez les avantages, inconvénients et les mesures à prendre pour contenir ces inconvénients. E. Super-transferts [15 minutes] L'entreprise de ventes de figurines de super-héros GARVEL a monté un partenariat avec les deux sites de ventes en ligne makemoney.com et dobusiness.com. Chaque entreprise lui demande de mettre à disposition respectivement un fichier CSV et un fichier XML pour le transfert du catalogue, stocké dans une base de données PostgreSQL. Modèle UML Figurines GARVEL (extrait) Les paramètres de connexion à la base sont : host=garvel.com dbname=cat user=customer password=public. port=5432 Les champs designation et prix sont présents tous les deux dans la vue vFigurine. Question 1 [Solution n°61 p 79] Réaliser un script PHP permettant de se connecter à la base PosgreSQL et d'afficher la désignation et le prix au format CSV, en suivant l'exemple ci-après. 1 2 3 4 Superman;15 Batman;12 Superchild;12 ... Indice : 1 <?php 39 Super-héros 2 3 4 5 6 7 8 $vConn = pg_connect("..."); $vSql ="..."; $vQuery=pg_query(...); while ($vResult = pg_fetch_array(...)) { echo "..."; } ?> Question 2 [Solution n°62 p 79] Réaliser un script PHP permettant de se connecter à la base et d'afficher la désignation et le prix selon un schéma XML, en suivant l'exemple ci-après. 1 2 3 4 5 6 <catalogue> <figurine designation='Superman' prix='1555'/> <figurine designation='Batman' prix='12'/> <figurine designation='Superchild' prix='12'/> ... </catalogue> Indice : 1 2 3 4 5 6 7 8 9 10 40 <?php $vConn = pg_connect("..."); $vSql ="..."; $vQuery=pg_query(...); echo "..."; while ($vResult = pg_fetch_array(...)) { echo "..."; } echo "..."; ?> IX - Friends IX Exercice 47 Exercice 49 A. Exercice [60 min] Soit la déclaration d'une base de données Oracle ci-après. 1 2 3 4 5 6 7 8 CREATE TABLE person ( login VARCHAR(10) NOT NULL, hasfriends NUMBER(10), isfriendof NUMBER(10), PRIMARY KEY (login), CHECK (hasfriends >= 0), CHECK (isfriendof >= 0) ); 1 2 3 4 5 CREATE TABLE friends ( login VARCHAR(10) REFERENCES person(login), declarefriend VARCHAR(10) REFERENCES person(login), PRIMARY KEY (login,declarefriend) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CREATE OR REPLACE TRIGGER tr_friends BEFORE INSERT ON friends FOR EACH ROW BEGIN -UPDATE person p SET p.hasfriends = NVL(p.hasfriends,0) + 1 WHERE p.login=:new.login; -UPDATE person p SET p.isfriendof = NVL(p.isfriendof,0) + 1 WHERE p.login=:new.declarefriend; -END; NB : Les explications devront être fournies dans un français concis, précis et correct. Question 1 [Solution n°63 p 79] Expliquez chaque instruction UPDATE du déclencheur tr_friends. Dessinez les 41 Friends tables person et friends après l'exécution de la séquence SQL ci-après. 1 2 3 4 5 6 7 INSERT INSERT INSERT INSERT INSERT INSERT SELECT INTO person (login) INTO person (login) INTO person (login) INTO friends VALUES INTO friends VALUES INTO friends VALUES * FROM person; VALUES ('a'); VALUES ('b'); VALUES ('c'); ('a','b'); ('a','c'); ('c','a'); Question 2 [Solution n°64 p 80] Expliquez pourquoi l'usage de la fonction NVL est indispensable dans le déclencheur tr_friends. Question 3 [Solution n°65 p 80] Rétro-concevez le modèle logique correspondant. Expliquez pourquoi friends est une association réflexive dissymétrique. Question 4 [Solution n°66 p 80] Expliquez ce que signifie chaque attribut de chaque relation. Question 5 [Solution n°67 p 80] Prouvez que ce modèle est en 3NF. Expliquez en quoi le modèle, bien qu'en 3NF, contient néanmoins de la redondance. Question 6 [Solution n°68 p 81] Rétro-concevez le modèle conceptuel correspondant. Utilisez les rôles pour expliciter les associations réflexives. Question 7 [Solution n°69 p 81] Décrivez les tables person et friends après l'exécution de la séquence SQL ciaprès. Expliquez pourquoi cela n'est pas cohérent, et ce qu'il faudrait faire pour le corriger (en français). 1 2 3 DELETE FROM friends WHERE login='a'; UPDATE friends SET declarefriend='b' WHERE login='c'; SELECT * FROM person; Question 8 [Solution n°70 p 81] Proposez une seconde version du déclencheur tr_friends permettant de traiter le cas précédent. Question 9 [Solution n°71 p 82] L'instruction ci-après engendre également une incohérence. déclencheur tr_person permettant de régler le problème. 1 42 Proposez INSERT INTO person (login, hasfriends, isfriendof) VALUES ('d',12,7); un Friends Question 10 [Solution n°72 p 82] Il reste encore un cas possible d'introduction d'incohérence lié à la redondance initiale, mentionnez-le et donnez un exemple d'instruction SQL posant problème. Question 11 [Solution n°73 p 82] Écrire deux fonctions permettant de renvoyer respectivement les valeurs réelles de hasfriends et isfriendof à partir de la table friends. CREATE FUNCTION f_hasfriends(v_login person.login%TYPE) RETURN person.hasfriends%TYPE CREATE FUNCTION f_isfriendof(v_login person.login%TYPE) RETURN person.isfriendof%TYPE Question 12 [Solution n°74 p 82] En utilisant les fonctions f_hasfriends et f_isfriendof, proposez une seconde version du déclencheur tr_person permettant de traiter ce dernier cas. B. Exercice [30min] On souhaite à présent proposer une implémentation alternative à l'implémentation précédente, en utilisant le relationnel-objet. L'enjeu est de ne plus introduire de redondance et donc de supprimer les déclencheurs contrôlant cette redondance. On remplacera les attributs hasfriends et isfriendof par les méthodes hasfriends() et isfriendof(). On profitera également du passage en RO pour : Utiliser le modèle imbriqué pour l'association N:M Utiliser les OID à la place des clés étrangères. Les deux requêtes ci-après devront donc donner les résultats associés. 1 2 SELECT p.login, f.friend.login FROM person p, TABLE(p.friends) f login friend.login a b a c c a Tableau 3 r1 1 2 SELECT p.login, p.hasfriends(), p.isfriendof() FROM person p login hasfriends() isfriendof() a 2 1 b 0 1 c 1 1 43 Friends Tableau 4 r2 Question [Solution n°75 p 83] Proposer une implémentation correspondant à r1 et r2. SQL3 sous Oracle et préalable pourra être insérer les données Indice : Une modélisation logique méthodologiquement. 44 RO posée pour s'aider X- E.T. X Exercice 52 Exercice 53 Un ufologue (quelqu'un qui étudie les ovnis et les extra-terrestres) décide de construire une base de données pour enregistrer les observations effectuées sur terre. Il pose le MCD suivant. Image 3 MCD L'indice de Roswell est obtenu par le calcul suivant : indice= nbyeux+ nbnez nbdoigts − nbbouches nbcouleurs Formule 1 Le premier extra-terrestre observé est Alf : il possède 2 yeux, 1 nez, 1 bouche il est brun et jaune et il possède 4 membres (2 mains et 2 pieds), de chacun 4 doigts. Son indice de Roswell est de -5. A. Exercice [30 minutes] Un ufologue (quelqu'un qui étudie les ovnis et les extra-terrestres) décide de construire une base de données pour enregistrer les observations effectuées sur terre. Il pose le MCD suivant. 45 E.T. Image 4 MCD L'indice de Roswell est obtenu par le calcul suivant : indice= nbyeux+ nbnez nbdoigts − nbbouches nbcouleurs Formule 2 Le premier extra-terrestre observé est Alf : il possède 2 yeux, 1 nez, 1 bouche il est brun et jaune et il possède 4 membres (2 mains et 2 pieds), de chacun 4 doigts. Son indice de Roswell est de -5. Cet exercice s'inscrit dans le cadre du modèle logique relationnel. Question 1 [Solution n°76 p 85] Traduire le MCD en relationnel. Question 2 [Solution n°77 p 85] Implémenter votre modèle R en SQL. Question 3 [Solution n°78 p 85] Ajouter l'extra-terrestre Alf dans la base de données. Indice : Assurez vous que toutes les informations concernant Alf sont bien insérées ou bien aucune, même en cas de panne au milieu du script (NX17, vous pouvez ignorer ce complément). Question 4 [Solution n°79 p 85] Écrivez la requête permettant d'afficher toutes les couleurs de Alf, puis la requête permettant de connaître le nombre de couleurs de Alf. Question 5 [Solution n°80 p 85] Écrivez la requête permettant d'afficher tous les membres de Alf, puis la requête permettant de connaître le nombre total de doigts de Alf. Question 6 [Solution n°81 p 86] Écrivez une fonction PHP permettant d'implémenter la méthode IndiceDeRoswell (la 46 E.T. fonction prend en entrée une connexion $vConn et un nom d'extra-terrestre $vNom). Indice : 1 2 3 4 5 function IndiceDeRoswell ($vConn, $vNom) { // Écriture et exécution des requêtes ... return vIndice ; } B. Exercice Cet exercice est la suite du précédent, mais il s'inscrit dans le cadre du modèle logique relationnel-objet. Question 1 [Solution n°82 p 86] Traduire le MCD en relationnel-objet. Indice : Complément au cours : En RO, les compositions sont gérées avec le modèle imbriqué, à l'instar des attributs multi-valués : en effet par définition l'élément composant ne peut être partagé par plusieurs composites, le modèle imbriqué n'introduit donc pas de redondance. 1. Créer un type correspondant au schéma de la table des composants 2. Créer une collection de ce type 3. Imbriquer cette collection dans la table des composites Question 2 [Solution n°83 p 86] Implémenter votre modèle RO en SQL3 sous Oracle (la méthode IndiceDeRoswell n'est pas implémentée à ce stade, seul son prototype est déclaré). Indice : La contrainte CHECK pour restreindre le type de membre à 'main' ou 'pied' est ignorée, l'attribut type est simplement instancié comme un CHAR(4). Question 3 [Solution n°84 p 87] Ajouter l'extra-terrestre Alf dans la base de données. Question 4 [Solution n°85 p 87] Écrivez la requête permettant d'afficher toutes les couleurs de Alf, puis la requête permettant de connaître le nombre de couleurs de Alf. Question 5 [Solution n°86 p 87] Écrivez la requête permettant d'afficher tous les membres de Alf, puis la requête permettant de connaître le nombre total de doigts de Alf. 47 E.T. Question 6 [Solution n°87 p 87] Écrivez la méthode IndiceDeRoswell renvoyant le résultat du calcul. Question 7 [Solution n°88 p 88] Écrivez la requête permettant d'afficher l'indice de Roswell de Alf. 48 Attributs multivalués XI - XI Documentum 55 Fichier CSV 56 Insertion de collections à partir de requêtes SELECT 58 Attributs multi-valués et relationnel 59 Attributs multi-valués et relationnel-objet 61 A. Documentum Le logiciel Documentum est un système de GED. Il s'appuie sur un SGBDR. Afin d'optimiser ses performances lors d'accès aux méta-données, il implémente une approche originale pour les attributs multi-valués. Cela consiste à créer une table pour les attributs mono-valués (de façon classique) et à ajouter une seconde table unique pour la gestion des attributs multi-valués. Image 5 Modèle de gestion des attributs dans Documentum 49 Attributs multi-valués Exemple : Attributs correspondants à l'exemple précédent. A ttributs V aleurs objec t_nam e Doc num éro 1 r_objec t_ty pe dm _doc um ent authors ­ V inc ent ­ Rém y k ey words ­ tes t ­ es s ai ­ doc Tableau 5 Exemples d'attributs gérés dans Documentum B. Fichier CSV Définition : Fichier CSV CSV est un format informatique permettant de stocker des données tabulaires dans un fichier texte. Chaque ligne du fichier correspond à une ligne du tableau. Les valeurs de chaque colonne du tableau sont séparées par un caractère de séparation, en général une virgule ou un point-virgule. Chaque ligne est terminée par un caractère de fin de ligne (line break). Toutes les lignes contiennent obligatoirement le même nombre de valeurs (donc le même nombre de caractères de séparation). Les valeurs vides doivent être exprimées par deux caractères de séparation contigus. La taille du tableau est le nombre de lignes multiplié par le nombre de valeurs dans une ligne. La première ligne du fichier peut être utilisée pour exprimer le nom des colonnes. Syntaxe 1 2 3 4 [NomColonne1;NomColonne2;...;NomColonneN] ValeurColonne1;ValeurColonne2;...;ValeurColonneN ValeurColonne1;ValeurColonne2;...;ValeurColonneN ... Exemple 1 2 3 4 Pierre;Dupont;20;UTC;NF17 Pierre;Dupont;20;UTC;NF26 Paul;Durand;21;UTC;NF17 Jacques;Dumoulin;21;UTC;NF29 Exemple 1 2 50 : Fichier CSV sans entête : Fichier CSV avec entête Prenom;Nom;Age;Ecole;UV Pierre;Dupont;20;UTC;NF17 Attributs multi-valués 3 4 5 Pierre;Dupont;20;UTC;NF26 Paul;Durand;21;UTC;NF17 Jacques;Dumoulin;21;UTC;NF29 Exemple 1 : Valeur nulle Jacques;Dumoulin;;UTC;NF29 L'âge est inconnu (NULL). Attention : Variations... La syntaxe des fichiers CSV n'est pas complètement standardisée, aussi des variations peuvent exister : Les chaînes de caractères peuvent être protégées par des guillemets (les guillemets s'expriment alors avec un double guillemet). Le caractère de séparation des nombres décimaux peut être le point ou la virgule (si c'est la virgule, le caractère de séparation doit être différent) ... Un des problème les plus importants reste l'encodage des caractères qui n'est pas spécifié dans le fichier et peut donc être source de problèmes, lors de changement d'OS typiquement. Méthode : Usage en base de données Les fichiers CSV sont très utilisés en BD pour échanger les données d'une table (export/import). Les SGBD contiennent généralement des utilitaires permettant d'exporter une table ou un résultat de requête sous la forme d'un fichier CSV, en spécifiant un certain nombre de paramètres (caractère de séparation de valeur, caractère de fin de ligne, présence ou non d'une ligne de définition des noms des colonnes, etc.). De même ils proposent des utilitaires permettant d'importer un fichier CSV dans une table (en spécifiant les mêmes paramètres), voire de créer directement une table à partir du fichier CSV (quand les noms des colonnes sont présents). Complément : Fichiers à largeur de colonne fixe Les fichiers à largeur de colonne fixe n'utilisent pas de séparateur de colonne, mais imposent le même nombre de caractères pour chaque cellule. L'avantage est de ne pas avoir à spécifier le caractère de séparation, l'inconvénient est la taille de fichier supérieure si les valeurs ne font pas toutes la même largeur. Complément : XML Les fichiers XML tendent de plus en plus à remplacer les fichiers CSV car ils permettent d'être beaucoup plus expressifs sur le schéma d'origine. Ils sont également plus standards (encodage spécifié, principe de séparation des données par les tags, etc.). Leur seul inconvénient est d'être plus verbeux et donc plus volumineux. Complément : Tables externes Certains SGBD, comme Oracle, permettent de créer des tables dites externes, qui autorisent de créer un schéma de table directement sur un fichier CSV, permettant ainsi un accès SQL standard à un fichier CSV, sans nécessité de l'importer d'abord dans une table. 51 Attributs multi-valués C. Insertion de collections à partir de requêtes SELECT Les constructeurs d'objets sont utilisables dans les instructions de type INSERT ... VALUES, mais ne peuvent être utilisés dans les instructions de type INSERT ... SELECT. Pour ces dernières une autre méthode de construction est nécessaire. Syntaxe : Insérer une collection d'objets depuis une table Soient les tables tro et t définies par le schéma RO ci-après. 1 2 3 type col : collection de <string> tro(a_obj:col) t(a:string) L'instruction suivante permet d'insérer le contenu de l'attribut a pour tous les enregistrements de t dans un seul attribut a_obj d'un seul enregistrement de tro sous la forme d'une collection. 1 2 INSERT INTO tro (a_objet) VALUES (CAST(MULTISET(SELECT a FROM t ) AS col)); L'instruction CAST (...) AS <type> permet de renvoyer un type donné à partir d'une expression. L'instruction MULTISET (...) permet de renvoyer une collection à partir d'une liste de valeurs. Méthode : Approche générale Soit la table tro définie par le schéma RO ci-après. Elle contient un attribut clé (pk_id) et un attribut qui est une collection (a_obj). 1 2 type col : collection de <string> tro(#pk_id:string, a_obj:col) Soit la table tr définie par le schéma relationnel ci-après. 1 tr(a:string, b:string) a1 b1 a1 b2 a1 b3 a2 b4 a2 b5 Tableau 6 Extrait de tr Si l'on souhaite insérer le contenu de tr dans tro de telle façon que les valeurs de a correspondent à pk_id et celles de b à obj_a, en insérant une ligne pour chaque valeur ax et que tous les bx correspondant soient stockés dans la même collection, il faut exécuter une requête INSERT ... SELECT qui fait appel aux instructions CAST et MULTISET. 52 Attributs multi-valués 1 2 3 4 5 6 7 8 9 10 INSERT INTO tro (pk_id, a_obj) SELECT a, CAST( MULTISET( SELECT b FROM tr tr1 WHERE tr1.a= tr2.a) AS col) FROM tr tr2; a1 (b1, b2, b3) a2 (b4, b5) Tableau 7 Extrait de tro après exécution de la requête INSERT D. Attributs multi-valués et relationnel [1h] Mise en contexte Jeune stagiaire dans une entreprise, vous vous voyez attribuer la mission suivante : dans le cadre de la migration d'un système de GED, vous devez récupérer des méta-données stockées dans un système Documentum pour les gérer dans une base PostgreSQL relationnelle classique. Le premier jour, vous vous voyez remettre deux fichiers CSV ("documents.csv" et "documentsMV.csv"), extractions de la base Documentum, dont les premières lignes sont données ci-après. documents.csv 1 2 3 4 object_id,titre,categorie,annee 9e8e,La Théorie de la relativité,Sciences,1923 9f5a,L'origine des espèces,Sciences,1859 9a0b,Le voyage au bout de la nuit,Littérature,1932 documentsMV.csv 1 2 3 4 5 6 7 object_id,auteurs,themes,langues 9e8e,Albert Einstein,Énergie nucléaire,Allemand 9e8e,,Physique quantique, 9f5a,,Théorie de l'évolution,Français 9f5a,,Vie,Anglais 9f5a,,Sélection naturelle, 9a0b, Louis-Ferdinand Céline,, Français Question 1 [Solution n°89 p 88] Établissez le modèle relationnel R1 d'une base de données qui pourra accueillir ces deux fichiers (représentez les clés existantes et précisez en quelle forme normale est ce modèle). Question 2 [Solution n°90 p 88] Proposez une formulation du problème sous la forme d'un modèle conceptuel en UML (en mobilisant la modélisation d'attribut multi-valué de façon classique). 53 Attributs multi-valués Question 3 [Solution n°91 p 89] Transformer ce modèle conceptuel en un modèle logique relationnel "classique" R2 (sans tenir compte de la particularité de gestion de Documentum, donc en transformant classiquement le schéma UML). Question 4 [Solution n°92 p 89] Écrivez le code SQL de création de R1 et de R2. Question 5 [Solution n°93 p 89] Écrivez la requête de migration des attributs mono-valués de R1 vers R2. Question 6 [Solution n°94 p 90] Écrivez les requêtes de migration des attributs multi-valués de R1 vers R2. Sécurisation de la migration Avant d'exécuter la migration, l'on vous demande de mettre en place des solutions pour la contrôler. Question 7 [Solution n°95 p 90] Afin de pouvoir vérifier la migration dans R2, écrivez une vue qui permet d'afficher un enregistrement pour chaque document de R1, avec tous ses attributs monovalués et pour chaque attribut multi-valué, le nombre de valeurs existantes (object_id, titre, categorie, annee, nbauteurs, nbthemes, nblangues). Question 8 [Solution n°96 p 90] Expliquez comment assurer que même en cas de panne la cohérence de la BD pourra être préservée lors de la migration de R1 vers R2. Complétez les requêtes de migration que vous avez réalisées à cet effet. Interface de recherche Une fois vos données migrées, il vous est demandé de réaliser un petit programme PHP permettant de faire une recherche dans la base PostgreSQL. Le principe du programme est le suivant : 1. Il affiche une page HTML avec un formulaire permettant de saisir un unique mot à rechercher à la fois dans le titre et parmi les auteurs. 2. Il appelle une page PHP qui se connecte à la base de données, exécute une requête SQL et affiche l'identifiant du ou des documents dont le titre ou un des auteurs comprend au moins ce mot. Question 9 [Solution n°97 p 90] Écrivez le fichier du formulaire HTML. Question 10 [Solution n°98 p 90] Écrivez, pour vous préparer à écrire le programme PHP, la requête SQL à exécuter au sein de celui-ci. Question 11 [Solution n°99 p 91] Écrivez le fichier PHP appelé par le formulaire HTML (pour les paramètres de connexion, vous pouvez reprendre ceux de l'UTC). 54 Attributs multi-valués E. Attributs multi-valués et relationnel-objet [1h] Mise en contexte Dans la perspective d'une implémentation sous Oracle 9i, vous devez étudier à présent la même réalisation en relationnel-objet. Question 1 [Solution n°100 p 91] Proposer un modèle logique relationnel-objet "classique" R3. Écrivez le code de création SQL3 de ce modèle. Indice : Utilisez le modèle imbriqué. Question 2 [Solution n°101 p 91] Écrivez l'instruction SQL de migration R1 vers R3. Indice : Utilisez la syntaxe INSERT ... SELECT avec CAST et MULTISET. Question 3 [Solution n°102 p 92] Écrivez une requête SQL permettant d'afficher les titres des documents (dans l'ordre alphabétique), ainsi que tous les thèmes associés à ce titre. L'on recherchera la forme ci-après afin d'envisager un nouvel export CSV. 1 2 3 4 5 6 L'origine des L'origine des L'origine des La Théorie de La Théorie de ... espèces,Théorie de l'évolution espèces,Vie espèces,Sélection naturelle la relativité,Énergie nucléaire la relativité,Physique quantique Proposer également une instruction SQL permettant d'optimiser la requête précédente. Indice : Pour obtenir un fichier CSV en sortie, utilisez l'opérateur de concaténation || : SELECT a || ',' || b ... 55 Solution des exercices > Solution n°1 (exercice p. 9) UML fautes solution > Solution n°2 (exercice p. 9) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Modèle(#code : char(8), nom : string, dateMiseEnMarché : date) avec dateMiseEnMarché {NOT NULL} Produit(#numeroSérie : number(6), #numéroProduit : char(4), annéeProduction : uint, modèle => Modèle) avec annéeProduction {NOT NULL} avec modèle {NOT NULL} Catégorie(#nom : string, description : string) Souscatégorie(#nom : string, description : string, catégorie => Catégorie) avec catégorie {NOT NULL} Faute(#code : string, titre : string, dateÉtablie : date, dateReparée : date, commentaire : string, produitSérie => Produit(numéroSérie), produitNuméro => Produit(numéorProduit)) avec titre {NOT NULL} avec dateÉtablie {NOT NULL} avec produitSérie {NOT NULL} avec produitNuméro {NOT NULL} FauteSouscatégorie(#faute => Faute(code), souscatégorie => Souscatégorie(nom)) 57 Solution des exercices 15 PROJ(Faute, code) IN PROJ(FauteSouscatégorie, faute) > Solution n°3 (exercice p. 9) 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 CREATE TABLE tModele ( code CHAR(8) PRIMARY KEY, nom VARCHAR NOT NULL, dateMiseEnMarche DATE ); CREATE TABLE tProduit ( numeroSerie NUMERIC(6) NOT NULL, numeroProduit CHAR(4) NOT NULL, anneeProduction INTEGER NOT NULL, modele CHAR(8) NOT NULL, PRIMARY KEY (numeroSerie, numeroProduit), FOREIGN KEY (modele) REFERENCES tModele(code) ON DELETE CASCADE ); CREATE TABLE tCategorie ( nom VARCHAR PRIMARY KEY, description VARCHAR ); CREATE TABLE tSouscategorie ( nom VARCHAR PRIMARY KEY, description VARCHAR ); CREATE TABLE tFaute ( code VARCHAR PRIMARY KEY, titre VARCHAR NOT NULL, dateEtablie DATE NOT NULL, dateReparee DATE, commentaire VARCHAR, produitSerie NUMERIC(6) NOT NULL, produitNumero CHAR(4) NOT NULL, FOREIGN KEY (produitSerie, produitNumero) REFERENCES tProduit(numeroSerie, numeroProduit) ON DELETE CASCADE ); CREATE TABLE tFauteSouscategorie ( faute VARCHAR NOT NULL, souscategorie VARCHAR NOT NULL, PRIMARY KEY (faute, souscategorie), FOREIGN KEY (faute) REFERENCES tFaute(code) ON DELETE CASCADE, FOREIGN KEY (souscategorie) REFERENCES tSouscategorie(nom) ON DELETE CASCADE ); > Solution n°4 (exercice p. 9) 58 1 2 3 4 5 6 7 SELECT m.nom, p.numeroSerie, COUNT(*) FROM tModele m, tProduit p, tFaute f WHERE m.code = p.modele AND p.numeroSerie = f.produitSerie AND p.numeroProduit = f.produitNumero GROUP BY m.nom, p.numeroSerie ; 1 SELECT m.nom, p.numeroSerie, COUNT(*) Solution des exercices 2 3 4 5 6 7 8 FROM (tModele m JOIN tProduit p ON m.code = p.modele) JOIN tFaute f ON (p.numeroSerie = f.produitSerie AND p.numeroProduit = f.produitNumero) GROUP BY m.nom, p.numeroSerie ; > Solution n°5 (exercice p. 11) Aucune, il n'y a pas de clé et l'attribut localisation n'est pas atomique, donc ce n'est même pas en 1NF. > Solution n°6 (exercice p. 11) 1 projection(jointure(personne,personne,localisation=localisation), nom) > Solution n°7 (exercice p. 11) 1 2 3 4 5 6 BEGIN TRANSACTION ; DELETE FROM personne_backup ; INSERT INTO personne_backup (nom, prenom, localisation, departement, unite) SELECT nom, prenom, localisation, departement, unite FROM personne ; COMMIT ; > Solution n°8 (exercice p. 11) 1 CREATE INDEX myindex ON Personne (Nom, Prenom) ; > Solution n°9 (exercice p. 11) 1 2 3 4 5 6 CREATE TRIGGER trPersonne BEFORE INSERT OR UPDATE OF departement ON Personne FOR EACH ROW BEGIN :new.departement=UPPER(:new.departement) ; END ; > Solution n°10 (exercice p. 13) 1 2 3 4 5 6 7 <?php //définition des variables de connexion à ajouter $conn=mysql_connect($host,$user,$passwd); mysql_select_db($bdd, $conn); $query = "SELECT nom, intitule FROM tPersonne, tMetier WHERE metier=code"; $result=mysql_query($query, $conn); echo "<html>"; 59 Solution des exercices 8 9 10 11 12 13 14 while($row = mysql_fetch_row($result)) { echo "<p>".$row[nom]." ".$row[intitule]."</p>"; } echo "</html>"; mysql_close($conn); ?> > Solution n°11 (exercice p. 13) 1 2 3 4 5 6 7 8 9 10 11 12 13 <?php //définition des variables de connexion à ajouter $conn=pg_connect($host,$user,$passwd,$bdd); $query = "SELECT nom, intitule FROM tPersonne, tMetier WHERE metier=code"; $result=pg_query($conn, $query); echo "<html>"; while($row = pg_fetch_row($result)) { echo "<p>".$row[nom]." ".$row[intitule]."</p>"; } echo "</html>"; pg_close($conn); ?> > Solution n°12 (exercice p. 13) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php //définition des variables de connexion à ajouter $conn=oci_connect($user, $passwd, $bd)) $query = "SELECT nom, intitule FROM tPersonne, tMetier WHERE metier=code"; $statement = oci_parse($conn, $query); oci_execute($statement); echo "<html>"; while($row = oci_fetch_array($statement)) { echo "<p>".$row[nom]." ".$row[intitule]."</p>"; } echo "</html>"; oci_close($conn); ?> > Solution n°13 (exercice p. 13) 1 2 3 4 5 6 7 8 Sub printMe() vSql = "SELECT nom, intitule FROM tPersonne, tMetier WHERE metier=code" Set vRs = CurrentDb.CreateQueryDef("", vSql).OpenRecordset For i = 1 To vRs.RecordCount Debug.Print vRs!nom & " " & vRs!intitule vRs.MoveNext Next i End Sub > Solution n°14 (exercice p. 14) 1 60 SET SERVEROUTPUT ON; Solution des exercices 2 3 4 5 6 7 8 9 10 11 12 13 14 DECLARE CURSOR printMe IS SELECT nom, intitule FROM tPersonne, tMetier WHERE metier=code; vNom tPersonne.nom%TYPE; vIntitule tMetier.intitule%TYPE; BEGIN OPEN printMe; LOOP FETCH printMe INTO vNom, vIntitule; EXIT WHEN printMe%NOTFOUND; DBMS_OUTPUT.PUT_LINE(' ' || vNom || ' ' || vIntitule); END LOOP; END; > Solution n°15 (exercice p. 14) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package nf17; import java.sql.*; import oracle.jdbc.OracleDriver; public class Exercice { public static void main(String[] args) { try { DriverManager.registerDriver (new OracleDriver()); Connection vCon = DriverManager.getConnection("jdbc:oracle:thin:nf17/nf17@localhost:152 1:test"); Statement vSt = vCon.createStatement(); String vSql = "SELECT nom, intitule FROM tPersonne, tMetier WHERE metier=code"; ResultSet vRs = vSt.executeQuery(vSql); while(vRs.next()) { System.out.println(vRs.getString(1)+" "+vRs.getString(2)); } } catch (Exception e) { e.printStackTrace(); } } } > Solution n°16 (exercice p. 20) Compte Opération num categorie ________________ =solde() date montant annee Dépense Recette Graphique 2 Modèle UML Il est également possible de faire intervenir une relation d'héritage à la place de l'attribut catégorie pour discriminer les comptes A et R. > Solution n°17 (exercice p. 21) Solution des exercices 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 CREATE TABLE Compte ( num INTEGER(2) PRIMARY KEY, categorie CHAR(1) CHECK (categorie='A' OR 'R') ); CREATE TABLE Recette ( num INTEGER(2) REFERENCES Compte(num), date DATETIME, montant DECIMAL(9,2), annee INTEGER(4), PRIMARY KEY (num, date) ) ; CREATE TABLE Depense ( num INTEGER(2) REFERENCES Compte(num), date DATETIME, montant DECIMAL(9,2), annee INTEGER(4), PRIMARY KEY (num, date) ) ; > Solution n°18 (exercice p. 22) S1 = RESTRICTION (Compte, categorie='R') S2 = JOINTURENATURELLE (S1, Depense) S3 = PROJECTION (S2, num) > Solution n°19 (exercice p. 22) 1 2 3 4 5 SELECT num FROM Recette GROUP BY num HAVING SUM(montant)>1000000 > Solution n°20 (exercice p. 22) 1 2 3 4 5 BEGIN TRANSACTION INSERT INTO Recette (222, '2006/06/20 12:02', 100, 2006) INSERT INTO Depense (125, '2006/06/20 12:02', 100, 2006) COMMIT TRANSACTION > Solution n°21 (exercice p. 25) 1 2 3 4 5 Type Operation <date:date, montant:reel, =annee():entier> Type listOperations collection de <Opération> Type Compte <num:entier, categorie:{A|R}, recettes:listOperations, depenses:listOperations, =solde():reel> tCompte de Compte (#num) > Solution n°22 (exercice p. 25) 62 Solution des exercices 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 CREATE TYPE Operation AS OBJECT ( date DATETIME, montant DECIMAL(9,2), MEMBER FUNCTION annee RETURN INTEGER ); / CREATE TYPE listOperations AS TABLE OF Operation; / CREATE TYPE Compte AS OBJECT ( num INTEGER(2), categorie CHAR(1), recettes listOperations, depenses listOperations, MEMBER FUNCTION solde RETURN DECIMAL ); / CREATE TABLE tCompte OF Compte ( PRIMARY KEY (num), CHECK (categorie = 'A' OR 'B') ) NESTED TABLE recettes STORE AS tRecettes, NESTED TABLE depenses STORE AS tDepenses; > Solution n°23 (exercice p. 25) 1 2 3 4 5 6 7 8 CREATE TYPE BODY Operation IS MEMBER FUNCTION annee RETURN INTEGER IS BEGIN RETURN YEAR(self.date) ; END ; END ; > Solution n°24 (exercice p. 25) 1 2 3 4 5 6 7 8 9 10 CREATE TYPE BODY Compte IS MEMBER FUNCTION solde RETURN INTEGER IS vR DECIMAL; vD DECIMAL; BEGIN SELECT SUM(R.montant) INTO vR FROM TABLE(self.recettes) R SELECT SUM(D.montant) INTO vD FROM TABLE(self.depenses) D RETURN vR-vD; END ; > Solution n°25 (exercice p. 25) 1 2 3 4 5 6 7 8 9 INSERT INTO tCompte (num, categorie, recettes) VALUES ( 1, 'A', listOperations( Operation(TO_DATE('20070720 11:31','YYYYMMDD HH:MI'), 100) ) ) ; > Solution n°26 (exercice p. 25) Solution des exercices 1 2 SELECT c.num, c.solde() FROM tCompte c; > Solution n°27 (exercice p. 26) MR2rc MR2ro Compte(num:entier, categorie:{A|R}) CompteSig(num=>Compte, signature:chaine) CompteProc(num=>Compte, procuration:chaine) Type listNoms collection de <Chaine> Type Compte <num:entier, categorie:{A|R}, procurations:listNoms> Table tCompte de Compte (num) signatures:listNoms, MR2rd Compte(num:entier, categorie:{A|R}) CompteMV(num=>Compte, signature:chaine, procuration:chaine) > Solution n°28 (exercice p. 26) Avantage : Moins de tables, donc moins de jointures lorsqu'il y a plusieurs attributs multi-valués Inconvénient : Apparition de valeurs nulles (l'attribut comportant le plus de valeurs détermine le nombre de lignes total dans la table des attributs mulivalués) > Solution n°29 (exercice p. 27) 1 2 3 4 diplome (#code:char(3), intitule:varchar) avec intitule KEY etudiant (#num:char(10), nom:varchar, origine=>diplome) avec origine NOT NULL uv (#code:char(5), titre:varchar) avec titre KEY note (#etu=>etudiant, #uv=>uv, #semestre:char(5), resultat:integer) > Solution n°30 (exercice p. 27) 1 2 3 4 5 6 7 CREATE VIEW vmax1 (uv, diplome, semestre, max) AS SELECT u.titre, d.intitule, n.semestre, max(n.resultat) FROM etudiant e, uv u, note n, diplome d WHERE e.num=n.etu AND u.code=n.uv AND e.origine=d.code GROUP BY u.titre, d.intitule, annee; > Solution n°31 (exercice p. 28) Le choix des clés primaires oblige à faire deux jointures dans la vue pour récupérer le titre des UV et intitulé des diplômes. En choisissant ces informations comme clé, on évite ces jointures. 64 Solution des exercices 1 2 3 4 etudiant (#num:char(10), nom:varchar, origine=>diplome) uv (#titre:varchar, code:char(5)) avec code KEY note (#etu=>etudiant, #uv=>uv, #semestre:char(5), note:integer) diplome (#intitule:varchar, code:char(3)) avec code KEY 1 2 3 4 5 CREATE VIEW vmax2 (uv, diplome, semestre, max) AS SELECT n.uv, e.origine, semestre, max(n.note) FROM etudiant e, note n WHERE e.num=n.etu GROUP BY n.uv, e.origine, annee; > Solution n°32 (exercice p. 28) 1 2 3 4 5 6 7 8 9 10 <html xmlns="http://www.w3.org/1999/xhtml"> <body> <?php $vConn=fConnect(); $vSql="SELECT * FROM vmax1 WHERE semestre='P2014' OR semestre='A2014'"; $vQuery=pg_query($vConn, $vSql); while ($vResult = pg_fetch_array($vQuery)) { echo "<p>$vResult['uv'] - vResult['diplome'] - vResult['semestre'] - vResult['max'] - </p>"; } ?> > Solution n°33 (exercice p. 28) 1 2 3 etudiant (#num:char(10), nom:varchar, origine=>diplome, #uv=>uv, #semestre:char(5), resultat:integer) avec num→nom, num→origine et avec intitule NOT NULL uv (#titre:varchar, code:char(5)) avec code KEY diplome (#intitule:varchar, code:char(3)) avec code KEY 1 2 3 4 CREATE VIEW vmax3 (uv, diplome, annee, max) AS SELECT uv, origine, annee, max(n.resultat) FROM etudiant GROUP BY uv, origine, annee; Remarque On aurait pu nommer la table dénormalisée note à la place de etudiant (par exemple). L'explicitation des DF est obligatoire Rappel Les trois principes à respecter pour introduire de la redondance dans une base de données (cf. Les trois principes à respecter pour introduire de la redondance dans une base de données) > Solution n°34 (exercice p. 28) 1 CREATE TRIGGER tretudiant 65 Solution des exercices 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 BEFORE INSERT OR UPDATE ON etudiant FOR EACH ROW DECLARE vnom etudiant.nom%TYPE; vorigine etudiant.origine%TYPE; BEGIN /** On récupère les informations éventuellement déjà existantes concernant l'étudiant inséré ou mis à jour ; on utilise la fonction max pour ne retourner qu'un seul tuple **/ SELECT max(e.nom), max(e.origine) INTO vnom, vorigine FROM etudiant e WHERE e.num=:new.num; /** On teste que ces valeurs sont les mêmes que celles nouvellement insérées ou mises à jour et sinon on insère une alterte dans la table 'etucontrole' **/ IF (:new.nom!=vnom OR :new.origine!=vorigine) THEN INSERT INTO etucontrole(num) VALUES (:new.num); END IF; END; > Solution n°35 (exercice p. 29) 1 2 3 4 5 6 7 8 9 CREATE TABLE etucontrole ( num char(10) REFERENCES etudiant(num) PRIMARY KEY ); SELECT e.num, e.nom, e.origine FROM etucontrole c, etudiant e WHERE c.num=e.num ORDER BY e.num; > Solution n°36 (exercice p. 29) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 66 BEGIN TRANSACTION; DELETE FROM etucontrole WHERE num='1'; UPDATE etudiant SET nom='Turing', origine='TC UTC' WHERE num='1'; COMMIT; BEGIN TRANSACTION; DELETE FROM etucontrole WHERE num='3'; UPDATE etudiant SET nom='von Neumann', origine='DUT Budapest' Solution des exercices 19 20 21 WHERE num='3'; COMMIT; Il faut corriger la table etudiant, et supprimer les incohérences de la liste gérée dans etucontrole. Les transactions permettent d’assurer l'atomicité de l'opération, sinon on pourrait, par exemple, supprimer les enregistrements de la table etucontrole, sans faire les correction dans etudiant. Le DELETE doit être avant le UPDATE, car le UPDATE pourra engendrer de nouvelles incohérences. > Solution n°37 (exercice p. 29) Solution avec le modèle imbriqué (table Notes imbriquée dans la table Étudiant, mais il est aussi possible d'imbriquer la table Notes dans la table UV). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 CREATE OR REPLACE TYPE typDiplome AS OBJECT ( code CHAR(8), intitule VARCHAR(255) ) ; / CREATE OR REPLACE TYPE typUV AS OBJECT ( code CHAR(8), titre VARCHAR(255) ) ; / CREATE OR REPLACE TYPE typNote AS OBJECT ( fk_UV REF typUV, semestre CHAR(5), resultat INTEGER ) ; / CREATE OR REPLACE TYPE typNotes AS TABLE OF typNote; / CREATE OR REPLACE TYPE typEtudiant AS OBJECT ( num CHAR(10), nom VARCHAR(255), notes typNotes, fk_origine REF typDiplome ) ; / CREATE TABLE Diplome OF typDiplome ( CONSTRAINT DIPLOME_PK PRIMARY KEY (code), CONSTRAINT INTITULE_UN UNIQUE (intitule), intitule NOT NULL ) ; / CREATE TABLE UV OF typUV ( CONSTRAINT UV_PK PRIMARY KEY (code), CONSTRAINT TITRE_UN UNIQUE (titre), titre NOT NULL ) ; / CREATE TABLE Etudiant OF typEtudiant ( CONSTRAINT ETUDIANT_PK PRIMARY KEY (num), fk_origine NOT NULL, SCOPE FOR (fk_origine) IS Diplome ) NESTED TABLE notes STORE AS NT_ETUDIANT_NOTES ; / ALTER TABLE NT_ETUDIANT_NOTES ADD (SCOPE FOR (fk_UV) IS UV) ; / Comme il n'est pas possible de passer des paramètres dans une instruction Nested table, le SCOPE FOR est exécuté a posteriori via un ALTER TABLE. Un jeu de données pour tester l'implémentation Solution des exercices 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 68 INSERT INTO Diplome (code, intitule) VALUES ('DUTINFO', 'DUT Informatique'); INSERT INTO Diplome (code, intitule) VALUES ('CPGEMATH', 'Classe préparatoire aux grandes écoles mathématiques, physique'); INSERT INTO Diplome (code, intitule) VALUES ('TC', 'Tronc commun'); INSERT INTO UV (code, titre) VALUES ('NF17', 'Conception de bases de données'); INSERT INTO UV (code, titre) VALUES ('NF26', 'Data Warehouse et Outils décisionnels'); INSERT INTO UV (code, titre) VALUES ('NF29', 'Ingénierie documentaire'); DECLARE ref_diplome_tc REF typDiplome; ref_diplome_dut REF typDiplome; ref_diplome_cpge REF typDiplome; ref_uv_nf17 REF typUV; ref_uv_nf26 REF typUV; ref_uv_nf29 REF typUV; BEGIN SELECT REF(d) INTO ref_diplome_dut FROM Diplome d WHERE d.code = 'DUTINFO' ; SELECT REF(d) INTO ref_diplome_cpge FROM Diplome d WHERE d.code = 'CPGEMATH' ; SELECT REF(d) INTO ref_diplome_tc FROM Diplome d WHERE d.code = 'TC' ; SELECT REF(u) INTO ref_uv_nf17 FROM UV u WHERE u.code = 'NF17' ; SELECT REF(u) INTO ref_uv_nf26 FROM UV u WHERE u.code = 'NF26' ; SELECT REF(u) INTO ref_uv_nf29 FROM UV u WHERE u.code = 'NF29' ; INSERT INTO Etudiant (num, nom, notes, fk_origine) VALUES ( '1', 'Crozat', typNotes( typNote(ref_uv_nf17, 'P2013', 6), typNote(ref_uv_nf26, 'P2014', 10), typNote(ref_uv_nf29, 'A2014', 8) ), ref_diplome_tc ); INSERT INTO Etudiant (num, nom, notes, fk_origine) VALUES ( '2', 'Dumas', typNotes( typNote(ref_uv_nf17, 'P2014', 16), typNote(ref_uv_nf29, 'A2014', 14) ), ref_diplome_cpge ); Solution des exercices 64 65 66 67 68 69 70 71 72 73 INSERT INTO Etudiant (num, nom, notes, fk_origine) VALUES ( '3', 'Vincent', typNotes( typNote(ref_uv_nf17, 'P2014', 21) ), ref_diplome_dut ); END ; Quelques requêtes pour afficher certaines informations de la base. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 -- Pour afficher quelques informations SELECT e.nom, n.fk_uv.titre as uv, n.semestre, n.resultat, e.fk_origine.intitule as origine FROM Etudiant e, TABLE(notes) n ORDER BY e.nom ; -- Affiche la moyenne de chaque étudiant SELECT e.nom, AVG(n.resultat) as moyenne FROM Etudiant e, TABLE(notes) n GROUP BY e.num, e.nom ORDER BY moyenne DESC; -- Affiche la moyenne des étudiants qui ont au moins trois notes SELECT e.nom, AVG(n.resultat) as moyenne FROM Etudiant e, TABLE(notes) n GROUP BY e.num, e.nom HAVING COUNT(n.resultat) > 2; Solution en gardant la classe d'association. 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 CREATE OR REPLACE TYPE typDiplome AS OBJECT ( code CHAR(8), intitule VARCHAR(255) ) ; / CREATE OR REPLACE TYPE typUV AS OBJECT ( code CHAR(8), titre VARCHAR(255) ) ; / CREATE OR REPLACE TYPE typEtudiant AS OBJECT ( num CHAR(10), nom VARCHAR(255), fk_origine REF typDiplome ) ; / CREATE OR REPLACE TYPE typNote AS OBJECT ( semestre CHAR(5), resultat INTEGER, fk_etudiant REF typEtudiant, fk_uv REF typUV ) ; / CREATE TABLE Diplome OF typDiplome ( CONSTRAINT DIPLOME_PK PRIMARY KEY (code), CONSTRAINT INTITULE_UN UNIQUE (intitule), intitule NOT NULL ) ; / CREATE TABLE UV OF typUV ( CONSTRAINT UV_PK PRIMARY KEY (code), CONSTRAINT TITRE_UN UNIQUE (titre), titre NOT NULL ) ; / CREATE TABLE Etudiant OF typEtudiant ( CONSTRAINT ETUDIANT_PK PRIMARY KEY (num), SCOPE FOR (fk_origine) IS Diplome, fk_origine NOT NULL ) ; Solution des exercices 41 42 43 44 45 46 47 / CREATE TABLE Note OF typNote ( SCOPE FOR (fk_etudiant) IS Etudiant, SCOPE FOR (fk_uv) IS UV, semestre NOT NULL ) ; / Il est impossible sous Oracle de créer une clé primaire constitué d'un attribut REF, nous ne pouvons donc pas fixer de clé primaire pour la table Note, alors qu'il faudrait dans l'idéal qu'elle soit constitué de fk_etudiant, fk_uv et semestre. Un jeu de données pour tester l'implémentation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 70 INSERT INTO Diplome (code, intitule) VALUES ('DUTINFO', 'DUT Informatique'); INSERT INTO Diplome (code, intitule) VALUES ('CPGEMATH', 'Classe préparatoire aux grandes écoles mathématiques, physique'); INSERT INTO Diplome (code, intitule) VALUES ('TC', 'Tronc commun'); INSERT INTO UV (code, titre) VALUES ('NF17', 'Conception de bases de données'); INSERT INTO UV (code, titre) VALUES ('NF26', 'Data Warehouse et Outils décisionnels'); INSERT INTO UV (code, titre) VALUES ('NF29', 'Ingénierie documentaire'); DECLARE ref_diplome_tc REF typDiplome; ref_diplome_dut REF typDiplome; ref_diplome_cpge REF typDiplome; BEGIN SELECT REF(d) INTO ref_diplome_dut FROM Diplome d WHERE d.code = 'DUTINFO' ; SELECT REF(d) INTO ref_diplome_cpge FROM Diplome d WHERE d.code = 'CPGEMATH' ; SELECT REF(d) INTO ref_diplome_tc FROM Diplome d WHERE d.code = 'TC' ; INSERT INTO Etudiant (num, nom, fk_origine) VALUES ( '1', 'Crozat', ref_diplome_tc ); INSERT INTO Etudiant (num, nom, fk_origine) VALUES ( '2', 'Dumas', ref_diplome_cpge ); INSERT INTO Etudiant (num, nom, fk_origine) VALUES ( '3', 'Vincent', ref_diplome_dut ); END ; / DECLARE ref_etudiant_1 REF typEtudiant; Solution des exercices 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 ref_etudiant_2 REF typEtudiant; ref_etudiant_3 REF typEtudiant; ref_uv_nf17 REF typUV; ref_uv_nf26 REF typUV; ref_uv_nf29 REF typUV; BEGIN SELECT REF(e) INTO ref_etudiant_1 FROM Etudiant e WHERE e.num = '1' ; SELECT REF(e) INTO ref_etudiant_2 FROM Etudiant e WHERE e.num = '2' ; SELECT REF(e) INTO ref_etudiant_3 FROM Etudiant e WHERE e.num = '3' ; SELECT REF(u) INTO ref_uv_nf17 FROM UV u WHERE u.code = 'NF17' ; SELECT REF(u) INTO ref_uv_nf26 FROM UV u WHERE u.code = 'NF26' ; SELECT REF(u) INTO ref_uv_nf29 FROM UV u WHERE u.code = 'NF29' ; INSERT INTO Note (semestre, resultat, fk_etudiant, fk_uv) VALUES ('P2013', 6, ref_etudiant_1, ref_uv_nf17) ; INSERT INTO Note (semestre, resultat, fk_etudiant, fk_uv) VALUES ('P2014', 10, ref_etudiant_1, ref_uv_nf26) ; INSERT INTO Note (semestre, resultat, fk_etudiant, fk_uv) VALUES ('A2014', 8, ref_etudiant_1, ref_uv_nf29) ; INSERT INTO ('P2014', 16, INSERT INTO ('A2014', 14, Note (semestre, ref_etudiant_2, Note (semestre, ref_etudiant_2, resultat, fk_etudiant, fk_uv) VALUES ref_uv_nf17) ; resultat, fk_etudiant, fk_uv) VALUES ref_uv_nf29) ; INSERT INTO Note (semestre, resultat, fk_etudiant, fk_uv) VALUES ('P2014', 21, ref_etudiant_3, ref_uv_nf17) ; END ; Quelques requêtes pour afficher certaines informations de la base. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 -- Pour afficher quelques informations SELECT n.fk_etudiant.nom as nom, n.fk_uv.titre as titre, n.semestre, n.resultat, n.fk_etudiant.fk_origine.intitule as origine FROM Note n ORDER BY nom; -- Affiche la moyenne de chaque étudiant SELECT n.fk_etudiant.nom as nom, AVG(n.resultat) as moyenne FROM Note n GROUP BY n.fk_etudiant.num, n.fk_etudiant.nom ORDER BY moyenne DESC; -- Affiche la moyenne des étudiants qui ont au moins trois notes SELECT n.fk_etudiant.nom as nom, AVG(n.resultat) as moyenne FROM Note n GROUP BY n.fk_etudiant.num, n.fk_etudiant.nom HAVING COUNT(n.resultat) > 2; 71 Solution des exercices > Solution n°38 (exercice p. 32) La seule clé possible est (DateOperation, NumCompte) puisque (NumCompte) détermine tous les attributs sauf MontantOperation et DateOperation et que (DateOperation, NumCompte) détermine MontantOperation. > Solution n°39 (exercice p. 32) Ce schéma est en 1NF (les attributs sont atomiques et il existe une clé (DateOperation, NumCompte)), mais pas en 2NF (car NumCompte est une partie de clé qui détermine des attributs non clé). > Solution n°40 (exercice p. 32) NumClient→Nom NumClient→Prenom NumCompte→NumClient NumCompte→Agence Agence→Ville Agence→Pays Pays→Monnaie DateOperation, NumCompte→MontantOperation > Solution n°41 (exercice p. 32) Image 6 Schéma relationnel décomposé > Solution n°42 (exercice p. 32) La réponse est triviale, en dehors de la détection de Operation comme entité faible de Compte. > Solution n°43 (exercice p. 32) 1 2 3 4 5 72 CREATE VIEW RTotalParPersonneEtParMonnaie (NumClient, Monnaie, SommeDeMontantOperation) AS SELECT TClient.NumClient, TPays.Monnaie, Sum(TOperation.MontantOperation) FROM TClient, TPays, TAgence, TCompte, TOperation WHERE TPays.Pays=TAgence.Pays Solution des exercices 6 7 8 9 AND TAgence.Agence=TCompte.Agence AND TClient.NumClient=TCompte.NumClient AND TCompte.NumCompte=TOperation.NumCompte GROUP BY TClient.NumClient, TPays.Monnaie; > Solution n°44 (exercice p. 33) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 INSERT INTO TPays SELECT DISTINCT Pays, Monnaie FROM TBank ; INSERT INTO TAgence SELECT DISTINCT Agence, Ville, Pays FROM TBank ; INSERT INTO TClient SELECT DISTINCT NumClient, Nom, Prenom FROM TBank ; INSERT INTO TCompte SELECT DISTINCT NumCompte, NumClient, Agence FROM TBank ; INSERT INTO TOperation SELECT NumCompte, DateOperation, MontantOperation FROM TBank Remarque : Ordre des instructions L'ordre des instructions est important afin de respecter les contraintes d'intégrité référentielle. Remarque : SELECT DISCTINCT Le SELECT DISTINCT est important puisque dans la relation initiale "à plat" ce sont les opérations qui sont représentées, et donc les pays, agences, clients et comptes sont potentiellement définis plusieurs fois (c'est même de là que provient le problème de redondance). On fait néanmoins l'hypothèse que la relation initiale ne contient pas d'erreurs, malgré l'absence de contrainte d'intégrité, et que par exemple la même opération n'est pas définie deux fois. > Solution n°45 (exercice p. 33) 1 2 3 4 5 6 7 8 9 10 11 CREATE TRIGGER trDeficit after insert on TOperation for each row BEGIN INSERT INTO TDeficit SELECT TOperation.NumCompte FROM TOperation WHERE TOperation.NumCompte=:new.NumCompte GROUP BY TOperation.NumCompte HAVING sum(TOperation.MontantOperation) < 0; END; 73 Solution des exercices > Solution n°46 (exercice p. 33) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public boolean NF17(Integer pNumClient) { try { DriverManager.registerDriver (new OracleDriver()); Connection vCon = DriverManager.getConnection( "jdbc:oracle:thin:Java/[email protected]. bank.com:1521:bank"); vCon.setAutoCommit(true); Statement vSt = vCon.createStatement(); ResultSet vRs = vSt.executeQuery("SELECT count(*) FROM TCompte, TDeficit WHERE TCompte.NumCompte=TDeficit.NumCompte AND TCompte.NumClient=" + pNumClient); vRs.next(); if (vRs.getInt(1)>0) { return true; } else { return false; } } catch (Exception e) { e.printStackTrace(); } > Solution n°47 (exercice p. 34) Le problème survient car la gestion des transactions n'est pas activée (mode AUTOCOMMIT ON). 1 2 3 4 5 6 7 8 9 10 SET AUTOCOMMIT OFF; CREATE procedure pVirement (Compte1 number, Compte2 number, Montant number, DateOp date) IS BEGIN INSERT INTO TOperation VALUES (Compte1, DateOp, Montant * -1); INSERT INTO TOperation VALUES (Compte2, DateOp, Montant); COMMIT; END; > Solution n°48 (exercice p. 34) 1 CREATE INDEX iMontant ON TOperation(MontantOperation); > Solution n°49 (exercice p. 35) 1 2 3 4 5 74 Personnage (#designation:string, prix:float, identiteSecrete:string, genre:{superhéro, supervilain}, mentor=>Personnage(designation)) Vehicule (#designation:string, prix:float, nbPlaces:integer, type: {terrestre, volant, aquatique}, conducteur=>Personnage(designation)) WITH conducteur NOT NULL Repaire (#designation:string, prix:float, emplacementGeographique:string, capacite:integer) Membre (#proprietaire=>Personnage(designation), #nom:{tête, bras gauche, bras droit, torse, jambe gauche, jambe droite}, couleur:string) Abrite (#repaire=>Repaire(designation), #personnage=>Personnage(designation)) Solution des exercices 6 - Constraint : PROJ(Personnage.designation) IN PROJ(Membre.proprietaire) Remarque : Héritage L'héritage est exclusif, un personnage ne peut pas être un véhicule ou un repaire, et la classe mère est abstraite. Il s'agit donc de l'un des cas simples de transformation : héritage par les classes filles. L'héritage est non complet, un héritage par la classe mère serait une erreur. L'héritage par référence est une mauvaise solution qui complexifie pour rien le modèle. Remarque : Clé candidate "identiteSecrete" Nous pourrions décider que identiteSecrete est une clé candidate, bien que la diagramme UML ne le précise pas. Remarque : Vue vFigurine La vue vFigurine est optionnelle, la classe mère étant abstraite. vFigurine = Union ( Union (Projection(Personnage,designation,prix), (Projection(Vehicule,designation,prix)), Projection(Repaire,designation,prix)) > Solution n°50 (exercice p. 36) 1 2 R1 = Restriction (Abrite, Abrite.repaire = 'La Ratcave') R = Jointure (Personnage, R1, R1.personnage = Personnage.designation) > Solution n°51 (exercice p. 36) 1 2 3 4 5 6 7 8 9 10 11 R1 = Restriction (Abrite, Abrite.repaire = 'GARVEL Tower') R2 = Restriction (Personnage, Personnage.mentor = 'Superman') RES1 = Jointure (R1, R2, R1.personnage = R2.designation) R3 = Restriction (Membre, Membre.nom = 'torse') RES2 = Jointure (RES1, R3, R3.proprietaire = RES1.designation) R4 = Restriction (Vehicule, Vehicule.type = 'aquatique') RES3 = Jointure (RES2, R4, R4.conducteur = RES2.designation) R = Projection (RES3, RES3.couleur) > Solution n°52 (exercice p. 36) 1 2 3 4 5 6 7 8 9 10 R1 = Restriction (Personnage, Personnage.prix < 10) RES1 = Projection (R1, R1.designation) R2 = Restriction (Vehicule, Vehicule.prix < 10) RES2 = Projection (R2, R2.designation) R3 = Restriction (Repaire, Repaire.prix < 10) RES3 = Projection (R3, R3.designation) R4 = Union (RES1, RES2) 75 Solution des exercices 11 R = Union (RES3, R4) Si la vue vFigurine a été préalablement créée 1 2 R1 = Restriction (vFigurine, vFigurine.prix < 10) R = Projection (R1, R1.designation) > Solution n°53 (exercice p. 36) 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 -- Figurine Type typeFigurine : <designation, prix> -- Véhicule Type typeVehicule : hérite de typeFigurine <type, nb_places> Vehicule de typeVehicule (#designation) -- Repaire Type typeRepaire : hérite de typeFigurine <emplacement_geographique, capacite> Repaire de typeRepaire (#designation) -- Membre Type typeMembre : <nom, couleur> Type listeMembres : collection de typeMembre -- References vers Repaire Type refRepaire : <fkRepaire =>o Repaire> Type listeRefRepaires : collection de refRepaire -- References vers Vehicule Type refVehicule : <fkVehicule =>o Vehicule> Type listeRefVehicules : collection de refVehicule -- Personnage Type typePersonnage : hérite de Figurine <identite_secrete, genre, mentor =>o Personnage, membres:listeMembres, repaires:listeRefRepaires, vehicules:listeRefVehicules> Personnage de typePersonnage(#designation) Remarque Les types non précisés sont ceux du diagramme UML. Remarque : Héritage L'héritage est exclusif et la classe mère est abstraite, on choisit un héritage par les classes filles. Remarque : Alternatives pour les associations Pour l'association N:M entre Repaire et Personne, on peut choisir d'imbriquer une collection de références vers Personnage dans la relation Repaire ; ou choisir une relation Abrite avec des références vers Repaire et Personnage. Pour l'association 1:N entre Véhicule et Personne, on peut choisir plus classiquement une référence depuis Vehicule vers Personnage. Mais les requêtes demandées ci-après sont plus simples à écrire avec les choix faits ici. > Solution n°54 (exercice p. 36) 76 Solution des exercices 1 2 3 SELECT p.designation, p.identite_secrete FROM Personnage p, TABLE(p.repaires) r WHERE r.designation = 'La Ratcave' ; > Solution n°55 (exercice p. 36) 1 2 3 4 5 6 SELECT m.couleur FROM Personnage p, TABLE(p.vehicules) v, TABLE(p.repaires) r, TABLE(p.membres) m WHERE r.designation = 'GARVEL Tower' AND p.mentor.designation = 'Superman' AND m.nom = 'torse' AND v.type = 'aquatique' ; > Solution n°56 (exercice p. 37) Explication 1. Le script est en mode AUTOCOMMIT ce qui signifie que chaque instruction est isolée dans une transaction propre, un COMMIT implicite étant exécuté après chaque INSERT (voir ci-après). 2. Une défaillance système (probablement une coupure réseau) est survenue juste avant le dernier INSERT, ce qui a empêché son exécution. Les six premiers INSERT ont donc été exécutés, mais pas le septième (celui qui concerne la tête). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 INSERT INTO Personnage (designation, prix, identite_secrete, genre) VALUES ('Superchild','12','Jordy','superhéros') ; COMMIT ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','tete','rose') ; COMMIT ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','bras droit','bleu') ; COMMIT ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','bras gauche','bleu') ; COMMIT ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','torse','rouge') ; COMMIT ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','jambe gauche','bleu') ; COMMIT ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','jambe droite','bleu') ; COMMIT ; > Solution n°57 (exercice p. 38) AUTOCOMMIT désactivé, BEGIN implicite Pour gérer ce problème il faut encapsuler les sept INSERT dans une unique transaction, celle-ci en assure alors l'atomicité : il ne sera plus possible à une partie seulement des INSERT d'être exécutée. 1 2 \set AUTOCOMMIT off INSERT INTO Personnage (designation, prix, identite_secrete, genre) VALUES ('Superchild','12','Jordy','superhéros') ; 77 Solution des exercices 3 4 5 6 7 8 9 INSERT INTO Membre (propriétaire, nom, ('Superchild','tete','rose') ; INSERT INTO Membre (propriétaire, nom, ('Superchild','bras droit','bleu') ; INSERT INTO Membre (propriétaire, nom, ('Superchild','bras gauche','bleu') ; INSERT INTO Membre (propriétaire, nom, ('Superchild','torse','rouge') ; INSERT INTO Membre (propriétaire, nom, ('Superchild','jambe gauche','bleu') ; INSERT INTO Membre (propriétaire, nom, ('Superchild','jambe droite','bleu') ; COMMIT ; couleur) VALUES couleur) VALUES couleur) VALUES couleur) VALUES couleur) VALUES couleur) VALUES AUTOCOMMIT activé, BEGIN explicite 1 2 3 4 5 6 7 8 9 BEGIN TRANSACTION ; INSERT INTO Personnage (designation, prix, identite_secrete, genre) VALUES ('Superchild','12','Jordy','superhéros') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','tete','rose') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','bras droit','bleu') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','bras gauche','bleu') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','torse','rouge') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','jambe gauche','bleu') ; INSERT INTO Membre (propriétaire, nom, couleur) VALUES ('Superchild','jambe droite','bleu') ; COMMIT TRANSACTION ; > Solution n°58 (exercice p. 38) 1. Les problèmes de performance sont liés au tri qui s'effectue sur le prix d'une part ; 2. et aux jointures qui doivent être exécutées entre toutes les tables de la base pour pouvoir extraire la liste de toutes les figurines avec leurs caractéristiques. > Solution n°59 (exercice p. 38) 1. Une première optimisation évidente concerne l'indexation du champ prix, elle permettra d'accélérer l'opération de tri, et ses effets indésirables (ralentissement de la mise à jour et augmentation du volume disque) seront négligeables. 2. Une approche relationnel-objet, centrée sur la table Personnage, permettrait de récupérer toutes les informations sans faire de jointure, en utilisant des OID et/ou la mécanique des nested tables. Elle réglerait donc, couplée avec l'indexation du champ prix, les problèmes de performance. > Solution n°60 (exercice p. 39) 1. Si l'on devait rester en relationnel on pourrait dénormaliser le modèle de façon à ne plus avoir à faire de jointure, ou à en faire moins selon le degré de dénormalisation. 2. Cette solution engendrerait de la redondance, qu'il faudrait contrôler par ailleurs, au niveau applicatif et/ou au niveau de la base de données, par des triggers par exemple. 3. Une alternative serait de créer une vue matérialisée qui pré-calculerait les 78 Solution des exercices jointures (et le tri par la même occasion). 4. L'inconvénient est que cette vue introduirait un décalage entre ce qui est présent dans la base et ce que l'on visualise sur le site. La calcul de la vue n'étant que de quelques secondes, on pourra envisager, sans surcharger le serveur, un nouveau calcul de la vue à chaque mise à jour des données liées, déclenchée par un trigger par exemple. (Notons que les solutions de partitionnement ne seraient d'aucun intérêt étant donné que toutes les données, enregistrements et attributs, doivent être retournées ; à moins d'une partition sur le prix cela aura même un effet négatif étant donné qu'il faudra réaliser l'union avant le tri par prix). > Solution n°61 (exercice p. 39) 1 2 3 4 5 6 7 8 <?php $vConn = pg_connect("host=garvel.com port=5432 dbname=cat user=customer password=public"); $vSql ="SELECT designation, prix FROM vFigurine;"; $vQuery=pg_query($vConn, $vSql); while ($vResult = pg_fetch_array($vQuery)) { echo "$vResult[designation];$vResult[designation]\n"; } ?> > Solution n°62 (exercice p. 40) 1 2 3 4 5 6 7 8 9 10 <?php $vConn = pg_connect("host=garvel.com port=5432 dbname=cat user=customer password=public"); $vSql ="SELECT designation, prix FROM vFigurine;"; $vQuery=pg_query($vConn, $vSql); echo "<catalogue>"; while ($vResult = pg_fetch_array($vQuery)) { echo "<figurine designation='$vResult[designation]' prix='$vResult[designation]'/>"; } echo "</catalogue>"; ?> > Solution n°63 (exercice p. 41) 1. La première instruction UPDATE ajoute 1 à l'attribut hasfriends de la personne qui déclare un nouvel ami 2. La seconde instruction UPDATE ajoute 1 à l'attribut isfriendof de la personne qui est déclarée comme ami login a b a c c a Tableau 8 friends login a declarefriend hasfriends 2 isfriendsof 1 79 Solution des exercices login hasfriends b isfriendsof 1 c 1 1 > Solution n°64 (exercice p. 42) Sous Oracle : "x + Null = Null". Ici hasfriends et isfriendof peuvent avoir la valeur Null à leur initialisation. La fonction NVL permet de remplacer ces attributs par 0 quand ils sont nuls, de façon à pourvoir les incrémenter une première fois. > Solution n°65 (exercice p. 42) 1 2 person (#login,hasfriends,isfriendof) friends (#login=>person, declarefriend=>person) friends exprime une association N:M réflexive car elle associe une classe avec elle même. Elle est dissymétrique car X peut déclarer Y comme ami, mais cela ne signifie pas que Y déclare X comme ami : (X,Y) n'implique pas (Y,X). > Solution n°66 (exercice p. 42) login login est l'identifiant d'une personne X hasfriends hasfriends est le nombre d'amis déclarés par X isfriendof isfriendof est le nombre de personnes ayant déclaré X comme ami Tableau 9 person login login est l'identifiant d'une personne X declarefriend declarefriend est la déclaration d'un ami de X Tableau 10 friends > Solution n°67 (exercice p. 42) DF Chaque relation a une clé et des attributs atomiques. Les deux seules DFE sont person.login → person.hasfriends et person.login → person.isfriendof. Toutes les DFE sont donc de type : une clé détermine un attribut. La relation est donc en BCNF, donc en 3NF. Il existe néanmoins de la redondance dans la mesure ou la connaissance de la relation friends permet de calculer hasfriends et isfriendof. > Solution n°68 (exercice p. 42) 80 Solution des exercices Modèle UML > Solution n°69 (exercice p. 42) login c declarefriend b Tableau 11 friends La table friends ne comportera plus qu'une ligne qui déclarera que 'b' est l'ami de 'c', mais la table person sera exactement dans le même état que précédemment, car il n'y a pas de modification de hasfriends et isfriendof lors de la suppression ou de la mise à jour, ce qui n'est pas cohérent. Il faut ajouter une décrémentation de ces attributs dans le déclencheur lors de la suppression et de la mise à jour dans friends. > Solution n°70 (exercice p. 42) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 CREATE OR REPLACE TRIGGER tr_friends BEFORE INSERT OR UPDATE OR DELETE ON friends FOR EACH ROW BEGIN -UPDATE person p SET p.hasfriends = NVL(p.hasfriends,0) + 1 WHERE p.login=:new.login; -UPDATE person p SET p.isfriendof = NVL(p.isfriendof,0) + 1 WHERE p.login=:new.declarefriend; -UPDATE person p SET p.hasfriends = p.hasfriends - 1 WHERE p.login=:old.login; -UPDATE person p SET p.isfriendof = p.isfriendof - 1 WHERE p.login=:old.declarefriend; -END; Remarque L'usage d'un IF dans le trigger serait plus rigoureux et plus performant, il permettrait d'éviter des UPDATE inutiles. 81 Solution des exercices > Solution n°71 (exercice p. 42) 1 2 3 4 5 6 7 8 CREATE OR REPLACE TRIGGER tr_person BEFORE INSERT ON person FOR EACH ROW BEGIN :new.hasfriends:=0; :new.isfriendof:=0; END; > Solution n°72 (exercice p. 43) Le dernier problème est celui de la mise à jour directe des attributs hasfriends et isfriendof. Exemple 1 2 3 UPDATE person SET hasfriends = 1 WHERE login = 'd'; > Solution n°73 (exercice p. 43) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 CREATE FUNCTION f_hasfriends(v_login person.login%TYPE) RETURN person.hasfriends%TYPE IS v_hasfriends person.hasfriends%TYPE ; BEGIN SELECT COUNT(*) INTO v_hasfriends FROM friends f WHERE f.login=v_login; RETURN v_hasfriends; END; -CREATE FUNCTION f_isfriendof(v_login person.login%TYPE) RETURN person.isfriendof%TYPE IS v_isfriendof person.isfriendof%TYPE; BEGIN SELECT COUNT(*) INTO v_isfriendof FROM friends WHERE declarefriend=v_login; RETURN v_isfriendof; END; > Solution n°74 (exercice p. 43) 1 2 3 4 5 6 7 8 9 82 CREATE OR REPLACE TRIGGER tr_person BEFORE INSERT OR UPDATE ON person FOR EACH ROW BEGIN :new.hasfriends:=f_hasfriends(:new.login); :new.isfriendof:=f_isfriendof(:new.login); END IF; END; Solution des exercices Complément 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 : Version plus performante CREATE OR REPLACE TRIGGER tr_person BEFORE INSERT OR UPDATE ON person FOR EACH ROW BEGIN -IF INSERTING THEN :new.hasfriends:=0; :new.isfriendof:=0; -ELSE :new.hasfriends:=f_hasfriends(:new.login); :new.isfriendof:=f_isfriendof(:new.login); -END IF; END; Cette version est plus performante car elle évite de parcourir la table friends sur les insertions, alors que l'on sait que les fonctions renverront 0. > Solution n°75 (exercice p. 44) 1 2 5 6 7 8 9 10 11 12 13 14 15 16 -CREATE OR REPLACE TYPE refperson_type AS OBJECT (friend REF person_type); -CREATE OR REPLACE TYPE reflistpersons_type AS TABLE OF refperson_type; -CREATE OR REPLACE TYPE person_type AS OBJECT ( login VARCHAR(10), friends reflistpersons_type, MEMBER FUNCTION hasfriends RETURN NUMBER, MEMBER FUNCTION isfriendof RETURN NUMBER ); -CREATE TABLE person OF person_type ( PRIMARY KEY (login) )NESTED TABLE friends STORE AS ntfriends; -- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 CREATE OR REPLACE TYPE BODY person_type IS -MEMBER FUNCTION hasfriends RETURN NUMBER IS v_hasfriends NUMBER; BEGIN SELECT count(f.friend.login) INTO v_hasfriends FROM person p, TABLE(p.friends) f WHERE p.login = SELF.login; RETURN v_hasfriends; END; -MEMBER FUNCTION isfriendof RETURN NUMBER IS v_isfriendof NUMBER; BEGIN SELECT count(p.login) INTO v_isfriendof FROM person p, TABLE(p.friends) f WHERE f.friend.login = SELF.login; RETURN v_isfriendof; END; -- 3 4 83 Solution des exercices 21 END; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 DECLARE v_a REF person_type; v_b REF person_type; v_c REF person_type; BEGIN -INSERT INTO person (login) VALUES ('a'); INSERT INTO person (login) VALUES ('b'); INSERT INTO person (login) VALUES ('c'); -SELECT REF(p) INTO v_a FROM person p WHERE login='a'; SELECT REF(p) INTO v_b FROM person p WHERE login='b'; SELECT REF(p) INTO v_c FROM person p WHERE login='c'; -UPDATE person SET friends = reflistpersons_type(refperson_type(v_b), refperson_type(v_c)) WHERE login = 'a'; UPDATE person SET friends = reflistpersons_type(refperson_type(v_a)) WHERE login = 'c'; -END; 23 24 25 26 27 28 84 Solution des exercices Remarque La solution proposée pour créer la base de données ne tient pas compte d'un problème de séquencement des déclarations, il faudrait utiliser un ALTER (ou un premier CREATE TYPE incomplet) pour traiter ce problème. > Solution n°76 (exercice p. 46) couleur(#fket=>et, #intitule:string) membre(#fket=>et, #numero:integer, type:{main|pied}, nbdoigts:integer) et(#nom:string, nbyeux:integer, nbnez:integer, nbbouches:integer) > Solution n°77 (exercice p. 46) 1 2 3 4 5 CREATE TABLE tET ( nom VARCHAR(20) PRIMARY KEY, nbyeux NUMBER(2), nbnez NUMBER(2), nbbouches NUMBER(2)); 1 2 3 4 CREATE TABLE tCouleur ( fket VARCHAR(20) REFERENCES tEt(nom), intitule VARCHAR(10), PRIMARY KEY (fket, intitule)); 1 2 3 4 5 6 CREATE TABLE tMembre ( fket VARCHAR(20) REFERENCES tEt(nom), numero NUMBER(10), type CHAR(4) CHECK (type='main' OR type='pied'), nbdoigts NUMBER(2), PRIMARY KEY (fket, numero)); > Solution n°78 (exercice p. 46) 1 2 3 4 5 6 7 8 INSERT INSERT INSERT INSERT INSERT INSERT INSERT COMMIT INTO INTO INTO INTO INTO INTO INTO ; tEt VALUES ('Alf',2,1,1); tCouleur VALUES ('Alf', 'Jaune'); tCouleur VALUES ('Alf', 'Brun'); tMembre VALUES ('Alf', 1, 'main', tMembre VALUES ('Alf', 2, 'main', tMembre VALUES ('Alf', 3, 'pied', tMembre VALUES ('Alf', 4, 'pied', > Solution n°79 (exercice p. 46) 1 2 3 SELECT intitule FROM tCouleur WHERE nom='Alf'; 1 2 3 SELECT count(*) FROM tCouleur WHERE nom='Alf'; > Solution n°80 (exercice p. 46) 1 SELECT tMembre.* 4); 4); 4); 4); Solution des exercices 2 3 FROM tMembre WHERE nom='Alf'; 1 2 3 SELECT sum(nbdoigts) FROM tMembre WHERE nom='Alf'; > Solution n°81 (exercice p. 46) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function IndiceDeRoswell($vConn, $vNom) { // Récupération des yeux, nez et bouches $vSql = "SELECT nbyeux, nbnez, nbbouches FROM tET WHERE nom='$vNom'"; $vQuery = pg_query($vConn,$vSql); $vResult = pg_fetch_array($vQuery, null, PGSQL_ASSOC); $nbyeux = $vResult[nbyeux]; $nbnez = $vResult[nbnez]; $nbbouches = $vResult[nbbouches]; // Récupération des couleurs $vSql = "SELECT count(*) AS nbcouleurs FROM tCouleur WHERE fket='$vNom'"; $vQuery = pg_query($vConn,$vSql); $vResult = pg_fetch_array($vQuery, null, PGSQL_ASSOC); $nbcouleurs = $vResult[nbcouleurs]; // Récupération des doigts $vSql = "SELECT sum(nbdoigts) AS nbdoigts FROM tMembre WHERE fket='$vNom'"; $vQuery = pg_query($vConn,$vSql); $vResult = pg_fetch_array($vQuery, null, PGSQL_ASSOC); $nbdoigts = $vResult[nbdoigts]; // Calcul de l'indice $vIndice = ($nbyeux+$nbnez)/$nbbouches-$nbdoigts/$nbcouleurs; return $vIndice; } Complément Il est possible de vérifier que $nbbouches et $nbcouleurs sont différents de 0 et que les autres paramètres ne sont pas NULL. > Solution n°82 (exercice p. 47) 1 2 3 4 5 Type Membre : <numero:string, type:{main|pied}, nbdoigts:integer> Type ListeMembres : Collection de <membre> Type ListeCouleurs : Collection de <string> Type Et <nom:string, nbyeux:integer, nbnez:integer, nbbouches:integer, couleurs:ListeCouleurs, membres:ListeMembres, =IndiceDeRoswell():real> Table tEt de ET (#nom) > Solution n°83 (exercice p. 47) 1 2 3 4 5 86 CREATE TYPE Membre AS OBJECT ( numero NUMBER(10), type CHAR(4), nbdoigts NUMBER(2) ); Solution des exercices 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 / CREATE TYPE ListeMembres AS TABLE OF Membre; / CREATE TYPE ListeCouleurs AS TABLE OF VARCHAR(10); / CREATE TYPE Et AS OBJECT ( nom VARCHAR(20), nbyeux NUMBER(2), nbnez NUMBER(2), nbbouches NUMBER(2), couleurs ListeCouleurs, membres ListeMembres, MEMBER FUNCTION IndiceDeRoswell RETURN REAL); / CREATE TABLE tEt OF Et ( PRIMARY KEY (nom)) NESTED TABLE couleurs STORE AS ntcouleurs, NESTED TABLE membres STORE AS ntmembres; > Solution n°84 (exercice p. 47) 1 2 3 4 5 6 7 8 INSERT INTO tEt VALUES ( 'Alf', 2, 1, 1, ListeCouleurs('brun', 'jaune'), ListeMembres(Membre(1,'main',4), Membre(2,'main',4), Membre(3,'pied',4), Membre(4,'pied',4)) ); > Solution n°85 (exercice p. 47) 1 2 3 SELECT nt.COLUMN_VALUE FROM tEt t, TABLE (t.couleurs) nt WHERE t.nom='Alf'; 1 2 3 SELECT count(nt.COLUMN_VALUE) FROM tEt t, TABLE (t.couleurs) nt WHERE t.nom='Alf'; > Solution n°86 (exercice p. 47) 1 2 3 SELECT nt.* FROM tEt t, TABLE (t.membres) nt WHERE t.nom='Alf'; 1 2 3 SELECT sum(nt.nbdoigts) FROM tEt t, TABLE (t.membres) nt WHERE t.nom='Alf'; > Solution n°87 (exercice p. 48) 1 2 CREATE OR REPLACE TYPE BODY Et IS 87 Solution des exercices 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 MEMBER FUNCTION IndiceDeRoswell RETURN REAL IS vNbcouleurs INTEGER; vNbdoigts INTEGER; vIndice REAL; BEGIN SELECT count(nt.COLUMN_VALUE) INTO vNbcouleurs FROM tEt t, TABLE (t.couleurs) nt WHERE t.nom=SELF.nom; SELECT sum(nt.nbdoigts) INTO vNbdoigts FROM tEt t, TABLE (t.membres) nt WHERE t.nom=SELF.nom; vIndice := ((SELF.nbyeux + SELF.nbnez) / SELF.nbbouches) (vNbdoigts / vNbcouleurs); RETURN vIndice; END; END; > Solution n°88 (exercice p. 48) 1 2 3 SELECT t.IndiceDeRoswell() FROM tEt t WHERE t.nom='Alf'; > Solution n°89 (exercice p. 53) 1 2 documents(#object_id,titre,categorie,annee) documentsMV(object_id=>documents,auteurs,themes,langues) Le modèle n'est pas en 1NF car la relation documentsMV n'a pas de clé. Remarque La solution documentsMV(#object_id=>documents,#auteurs,#themes,#langues) n'est pas correcte car les attributs auteurs,themes et langues peuvent être nuls. > Solution n°90 (exercice p. 53) documents #object_id titre categorie annee auteurs[0..N] themes[0..N] langues[0..N] Tableau 12 Modèle UML de la classe document Attention Il ne faut pas sur-interpréter les données présentes, nous sommes certains que les données sont des attributs (mono et multi-valués). En effet les données étaient ainsi modélisées dans la base Documentum existante. Les modéliser comme des classes - par exemple - serait donc une erreur puisqu'une interprétation qui n'aurait 88 Solution des exercices pas de sens avec les données dont nous disposons. > Solution n°91 (exercice p. 54) 1 2 3 4 documents(#object_id,titre,categorie,annee) documentsAuteurs(#object_id=>documents,#auteurs) documentsThemes(#object_id=>documents,#themes) documentsLangues(#object_id=>documents,#langues) > Solution n°92 (exercice p. 54) 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 CREATE TABLE R1_documents ( object_id VARCHAR(4), titre VARCHAR(100), categorie VARCHAR(50), annee INTEGER(4), PRIMARY KEY (object_id) ); CREATE TABLE R1_documentsMV ( object_id VARCHAR(4), auteurs VARCHAR(50), themes VARCHAR(50), langues VARCHAR(20), FOREIGN KEY (object_id) REFERENCES R1_documents(object_id) ); CREATE TABLE R2_documents ( object_id VARCHAR(4), titre VARCHAR(100), categorie VARCHAR(50), annee INTEGER(4), PRIMARY KEY (object_id) ); CREATE TABLE R2_documentsAuteurs object_id VARCHAR(4), auteurs VARCHAR(50), PRIMARY KEY (object_id, auteurs), FOREIGN KEY (object_id) REFERENCES R1_documents(object_id) ); CREATE TABLE R2_documentsThemes object_id VARCHAR(4), themes VARCHAR(50), PRIMARY KEY (object_id, themes), FOREIGN KEY (object_id) REFERENCES R1_documents(object_id) ); CREATE TABLE R2_documentsLangues object_id VARCHAR(4), langues VARCHAR(50), PRIMARY KEY (object_id, langues), FOREIGN KEY (object_id) REFERENCES R1_documents(object_id) ); > Solution n°93 (exercice p. 54) 1 2 3 INSERT INTO R2_documents (object_id,titre,categorie,annee) SELECT object_id,titre,categorie,annee FROM R1_documents; > Solution n°94 (exercice p. 54) 89 Solution des exercices 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 INSERT INTO R2_documentsAuteurs (object_id,auteurs) SELECT object_id,auteurs FROM R1_documentsMV WHERE auteurs IS NOT NULL ; INSERT INTO R2_documentsThemes (object_id,themes) SELECT object_id,themes FROM R1_documentsMV WHERE themes IS NOT NULL ; INSERT INTO R2_documentsLangues (object_id,langues) SELECT object_id,langues FROM R1_documentsMV WHERE langues IS NOT NULL ; > Solution n°95 (exercice p. 54) 1 2 3 4 5 CREATE VIEW vR1_documents (object_id, titre, categorie, annee, nbauteurs, nbthemes, nblangues) AS SELECT d.object_id, d.titre, d.categorie, d.annee, count(mv.auteurs), count(mv.themes), count(mv.langues) FROM R1_documents d INNER JOIN R1_documentsMV mv ON d.object_id=mv.object_id GROUP BY d.object_id, d.titre, d.categorie, d.annee; > Solution n°96 (exercice p. 54) Il faut en-capsuler les 4 requêtes de migration dans une transaction. Ainsi soit toutes les instructions seront correctement exécutées, soit aucune. En cas de panne pendant la migration, le SGBD annulera ce qui a été commencé pour se retrouver dans l'état avant la migration. Il suffit de désactiver le mode AUTOCOMMIT s'il est activé et d'ajouter l'instruction COMMIT à la fin des quatre requêtes INSERT servant à la migration. > Solution n°97 (exercice p. 54) formulaire.html 1 2 3 4 5 6 7 8 9 <html> <body> <p>Saisissez un mot à rechercher dans le titre ou les auteurs des documents :</p> <form method="GET" action="recherche.php"> <input type="text" name="mot"/> <input type="submit"> </form> </body> </html> > Solution n°98 (exercice p. 54) $vSql 1 90 SELECT DISTINCT d.object_id AS id Solution des exercices 2 3 4 FROM R2_documents d LEFT JOIN R2_documentsAuteurs a ON d.object_id=a.object_id WHERE d.titre LIKE '%$vMot%' OR a.auteurs LIKE '%$vMot%' Alternative 1 2 3 4 5 6 7 SELECT object_id AS id FROM R2_documents WHERE titre LIKE '%$vMot%' UNION SELECT object_id AS id FROM R2_documentsAuteurs WHERE auteurs LIKE '%$vMot%' > Solution n°99 (exercice p. 54) recherche.php 1 2 3 4 5 6 7 8 9 10 11 12 <? php $vMot = $_GET[mot] ; $vConnect = pg_connect("host=... port=... dbname=... user=... password=...") ; $vSql = "SELECT ..." ; $vQuery=pg_query($vConnect, $vSql) ; while ($vResult = pg_fetch_array($vQuery, null, PGSQL_ASSOC)) { echo "<p>" ; echo $vResult["id"] ; echo "</p>" ; } pg_close($vConnect) ; ?> > Solution n°100 (exercice p. 55) Modèle RO 1 2 type l_termes : collection de <string> documents(#object_id:string, titre:string, categorie:string ,annee:string, auteurs:l_termes, themes:l_termes, langues:l_termes) Code SQL 1 2 3 4 5 6 7 8 9 10 11 12 13 CREATE TYPE l_termes AS TABLE OF varchar(50) ; CREATE TABLE R3_documents ( object_id VARCHAR(4), titre VARCHAR(100), categorie VARCHAR(50), annee INTEGER(4), auteurs l_termes, themes l_termes, langues l_termes, PRIMARY KEY (object_id)) NESTED TABLE auteurs STORE AS ntauteurs, NESTED TABLE themes STORE AS ntthemes, NESTED TABLE langues STORE AS ntlangues ; > Solution n°101 (exercice p. 55) 1 2 3 4 INSERT INTO R3_documents (object_id,titre,categorie,annee, auteurs, themes, langues) SELECT d.object_id, d.titre, d.categorie, d.annee, CAST( Solution des exercices 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 MULTISET( SELECT auteurs FROM R1_documentsMV dmv WHERE auteurs IS NOT NULL AND dmv.object_id=d.object_id) AS l_termes), CAST( MULTISET( SELECT themes FROM R1_documentsMV dmv WHERE themes IS NOT NULL AND dmv.object_id=d.object_id) AS l_termes), CAST( MULTISET( SELECT langues FROM R1_documentsMV dmv WHERE langues IS NOT NULL AND dmv.object_id=d.object_id) AS l_termes) FROM R1_documents d; > Solution n°102 (exercice p. 55) Affichage 1 2 3 SELECT titre || ',' || t.COLUMN_VALUE FROM R3_documents d, TABLE(d.themes) t ORDER BY titre Optimisation 1 92 CREATE INDEX idx_titre ON R3_documents (titre); Solution des exercices > Solution n°1 (exercice p. 15) <?php $conn=pg_connect($host,$user,$passwd,$bdd); $query = "SELECT A FROM T"; $result=pg_query($conn, $query); echo "<html>"; while($row = pg_fetch_row($result)) { echo "<p>$row[A]</p>"; } echo "</html>"; pg_close($conn); ?> > Solution n°2 (exercice p. 15) <?php $conn=mysql_connect($host,$user,$passwd); mysql_select_db($bdd, $conn); $query = "SELECT A FROM T"; $result=mysql_query($query, $conn); echo "<html>"; while($row = mysql_fetch_row($result)) { echo "<p>$row[A]</p>"; } echo "</html>"; mysql_close($conn); ?> > Solution n°3 (exercice p. 16) <?php //définition des variables de connexion à ajouter $conn=oci_connect($user, $passwd, $bd)) 93 Solution des exercices $query = "SELECT A FROM T"; $statement = oci_parse($conn, $query); oci_execute($statement); echo "<html>"; while($row = oci_fetch_array($statement)) { echo "<p>$row[A]</p>"; } echo "</html>"; oci_close($conn); ?> > Solution n°4 (exercice p. 16) Sub printA() vSql = "SELECT A FROM T" Set vRs = CurrentDb.CreateQueryDef("", vSql).OpenRecordset For i = 1 To vRs.RecordCount Debug.Print vRs!A vRs.MoveNext Next i End Sub > Solution n°5 (exercice p. 17) SET SERVEROUTPUT ON; DECLARE CURSOR cT IS SELECT A FROM T; vA T.A%TYPE; BEGIN OPEN cT; LOOP FETCH cT INTO vA; EXIT WHEN cT%NOTFOUND; DBMS_OUTPUT.PUT_LINE(vA); END LOOP; END; > Solution n°6 (exercice p. 17) package nf17; import java.sql.*; import oracle.jdbc.OracleDriver; public class Exercice { public static void main(String[] args) { try { DriverManager.registerDriver (new OracleDriver()); Connection vCon 94 = Solution des exercices DriverManager.getConnection("jdbc:oracle:thin:nf17/nf17@localhost:1521:test"); Statement vSt = vCon.createStatement(); String vSql = "SELECT A FROM T"; ResultSet vRs = vSt.executeQuery(vSql); while(vRs.next()) { System.out.println(vRs.getString(1)); } } catch (Exception e) { e.printStackTrace(); } } } > Solution n°7 (exercice p. 20) C.num→C.categorie C.categorie→C.num (R.num, R.date)→R.montant, R.annee R.num→R.date R.annee→R.date R.date→R.annee (D.num, D.date)→D.montant, D.annee D.num→D.date D.annee→D.date D.date→D.annee > Solution n°8 (exercice p. 21) Oui, le schéma est en 2NF Non, le schéma est seulement en 1NF 95 Solution des exercices R.date→R.annee donc une partie de clé détermine un attribut non-clé. > Solution n°9 (exercice p. 21) Compte (num:entier, categorie:{A|R}, =solde():reel) Recette(num=>Compte, date=>AnneeR, montant:reel) AnneeR(date:date, annee:entier) Depense(num=>Compte, date=>AnneeD, montant:reel) AnneeD(date:date, annee:entier) Compte (num:entier, categorie:{A|R}, =solde():reel) Recette(num=>Compte, date=>Annee, montant:reel) Depense(num=>Compte, date=>Annee, montant:reel) Annee(date:date, annee:entier) Compte (num:entier, categorie:{A|R}, =solde():reel) Recette(num=>Compte, date:date, montant:reel) Depense(num=>Compte, date:date, montant:reel) 1. La première solution est techniquement correcte, c'est le résultat d'une décomposition classique. Mais la duplication des tables AnneeD et AnneeR en fait une mauvaise solution. C'est la plus mauvaise des trois. 2. La seconde solution est le résultat d'une décomposition, puis d'une mutualisation des tables AnneeD et AnneeR. Elle est correcte, et meilleure que la précédente, bien que annee puisse être calculé à partir de date, sans en fait avoir besoin de table du tout. 3. C'est la meilleure solution, qui consiste à redéfinir annee comme un attribut dérivé de date et à ne pas l'implémenter en relationnel. > Solution n°10 (exercice p. 22) Indexation Partitionnement horizontal Partitionnement vertical Dénormalisation Vue matérialisée Étant donné que les informations doivent être présentées seulement chaque matin, la vue matérialisée, qui sera recalculée chaque nuit est la meilleure solution. L'indexation est inutile, les autres solutions auront des inconvénients que ne présente pas la vue matérialisée dans ce cas. > Solution n°11 (exercice p. 23) Function solde(pNum As Integer) As Real Dim vCodeSql As String 96 Solution des exercices Dim vR As Real Dim vD As Real vCodeSql = "SELECT SUM(montant) FROM Recette WHERE num=" & pNum vR = CurrentDb.CreateQueryDef("", vCodeSql).OpenRecordset.Fields(0) vCodeSql = "SELECT SUM(montant) FROM Depense WHERE num=" & pNum vD = CurrentDb.CreateQueryDef("", vCodeSql).OpenRecordset.Fields(0) solde=vR-vD End Function > Solution n°12 (exercice p. 23) CREATE TRIGGER RecetteTrig BEFORE INSERT OR UPDATE ON Recette FOR EACH ROW BEGIN :new.annee=YEAR(:new.date) ; END ; Le trigger permet de contrôler la redondance et d'assurer que la cohérence des données est maintenue. > Solution n°13 (exercice p. 24) <html xmlns="http://www.w3.org/1999/xhtml"> <body> <?php /* Initialisation */ $vserveur="monassos.free.fr" ; $vport="5432" ; $vcu="president" ; $vbase="bdassos" ; $vcp="bonjour" ; /* Connexion à la BD */ $c = pg_connect("host=$vserveur port=$vport dbname=$vbase password=$vcp"); /* Exécution de la requête*/ $sql = "INSERT INTO Compte (".$_GET["n"].",'".$_GET["c"]."')" ; pg_query($c, $sql) ; /* Finalisation */ pg_close($c); echo "<p>Insertion du compte numéro".$_GET["n"]."effectuée</p>" ; ?> </body> </html> user=$vcu 97 Glossaire Constructeur d'objet En programmation orientée objet, un constructeur d'objet est une méthode particulière d'une classe qui permet d'instancier un objet de cette classe. L'appel à cette méthode de classe a donc pour conséquence la création d'un nouvel objet de cette classe. 99 Signification des abréviations - BD CSV DF GED OS SGBD SGDBR XML Base de Données Comma Separated Values Dépendance Fonctionnelle Gestion Électronique de Documents Operating Système (Système d'Exploitation) Système de Gestion de Bases de Données Système de Gestion de Bases de Données Relationnelles eXtensible Markup Language 101 Index Association........................ p. Dénormalisation................. p. Relationnel........................ p. Attribut............................. p. Normalisation.................... p. UML p. Classe............................... p. Redondance...................... p. 103