Correction TD2 Cette correction, adaptée à PL/SQL, est principalement à mettre au crédit de Cécile Capponi, responsable du cours Base de Données en télé-enseignement. Des fichiers créant la base ainsi que quelques données se trouvent à l'adresse http://www.lif.univmrs.fr/~reyraud/BD/Hotel_Oracle.tar.gz.. mrs.fr/~reyraud/BD/Hotel_Oracle.tar.gz Toutes les corrections corrections indiquées ici sont des exemples de corrections corrections : il existe toujours d'autres possibilités. Par ailleurs, n'ayons pas traité en cours la récupération des erreurs SQL dans le cadre des fonctions PL/SQL, les corrections données ici supposent qu'aucune erreur SQL n'est produite. Or, Or, dans dans cert certai ains ns cas, cas, des des erreu erreurs rs peuv peuven entt surv surveni enirr : nous nous ne les les trai traito tons ns pas, pas, mais mais une implantation parfaite des fonctions devrait anticiper toute tout e erreur SQL et la traiter. Nous travaillons sur la base dont le schéma est le suivant : hotel(numhotel, hotel(numhotel, nom, ville, etoiles) chambre(numchambre, numhotel , etage, type, prixnuitht) client(numclient, nom, prenom) occupation(numoccup, occupation(numoccup, numclient , numchambre, numchambre, numhotel , datearrivee, datedepart) reservation(numresa, numclient , numchambre, numchambre, datearrivee, datedepart) Question 1. Supprimer tous les clients n'ayant jamais occupé de chambre, et n'ayant aucune
réservation en cours. Le plus "difficile" ici est d'écrire la condition de suppression des clients. Il s'agit en fait de supprimer les clients qui n'en sont pas vraiment, puisqu'ils n'ont jamais occupé de chambre et qu'ils qu'ils n'en n'en réserv réservent ent pas. pas. Il s'agit s'agit donc des clients clients dont le numéro numéro n'appar n'apparaît aît dans aucune réservation ni dans aucune occupation de chambre. La requête suivante nous renvoie le tableau des numéros de clients qui n'apparaissent dans aucune des tables occupation et reservation. reservation. SELECT numclient FROM client MINUS ( SELECT numclient FROM occupation UNION SELECT numclient FROM reservation );
Il nous reste donc à effacer les clients dont le numéro est une ligne de la relation relation ci-dessus. ci-dessus. Pour cela, nous exécutons la commande (en italique, nous indiquons où se trouve la sélection des clients concernés) : DELETE FROM client WHERE numclient IN ( SELECT numclient FROM client MINUS ( SELECT numclient FROM occupation UNION SELECT numclient FROM reservation )
);
Il est possible (et meilleur) de faire un petit peu plus simple, en considérant que la condition sur le DELETE peut être formée d'un NOT IN. IN. Dans ce cas, le MINUS n'est pas requis dans la requête de sélection des numéros de clients, seule la seconde partie est valable : on supprime les clients dont le numéro n'est pas dans les numéros de clients ayant effectué une réservation ou occupé une chambre : DELETE FROM client WHERE numclient NOT IN ( SELECT numclient FROM occupation UNION SELECT numclient FROM reservation )
);
Question 2. Ajouter une étoile à l'Hotel terminus de Grenoble.
Pour ajouter une étoile, il faut utiliser la commande UPDATE qui ajoute 1 à la valeur courante de etoiles pour le tuple vérifiant la condition sur le nom et la ville.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
UPDATE hotel SET etoiles = etoiles+1 WHERE nom = 'Hotel terminus' AND ville='Grenoble'; Question 3. Augmenter de 12% le prix de toutes les chambres à Nice.
Là aussi nous utilisons la commande UPDATE sur les tuples de la table chambre. La sélection des chambres concernées (celles de Nice) nécessite la sélection de tous les hôtels de Nice : la clause WHERE vérifie que le numéro d'hôtel de la chambre à modifier fait bien partie du tableau des numéros d'hôtels dont la ville est 'Nice'. Il est inutile de passer par les numéros de chambres, seule une condition sur le numéro de l'hôtel est suffisante. UPDATE chambre SET prixnuitht = prixnuitht + prixnuitht*12/100 WHERE numhotel IN ( SELECT numhotel FROM hotel WHERE ville = 'Nice'); Question 4. Ecrire une fonction qui accepte en paramètre le nom et la ville d'un l'hôtel et qui
retourne le nombre d'étoiles de cet hôtel. La principale difficulté dans l'écriture de cette fonction est le type de ses paramètres et son type de retours. Comme nous ne les connaissons pas de manière détaillée, nous utilisons %TYPE. A noter que l'on ne peut pas retourner directement le résultat d'une requête et qu'il nous faut donc utiliser une variable (noter resultat (noter resultat ici) ici) CREATE OR REPLACE FUNCTION nb_etoiles(nom_hotel hotel.nom%TYPE, ville_hotel hotel.ville%TYPE) hotel.ville%TYPE) RETURN hotel.etoile%TYPE IS -- déclaration de la variable resultat hotel.etoile%TYPE; BEGIN SELECT etoile INTO resultat FROM hotel WHERE nom = nom_hotel AND ville = ville_hotel; RETURN resultat; END; Question 5. ●
Ajouter deux colonnes dans la table hotel , pour stocker les congés annuels de l'hôtel : ces colonnes doivent stocker la date de début et la date de fin du congé annuel. ALTER TABLE hotel ADD debutconges Date DEFAULT CURRENT_DATE(); CURRENT_DATE(); ALTER TABLE hotel ADD finconges Date DEFAULT CURRENT_DATE(); CURRENT_DATE();
Afin de ne pas se retrouver avec des colonnes ne contenant que la valeur NULL, on peut spécifier que la date par défaut soit celle d'aujourd'hui. Dans un esprit de gestion de la cohérence de la base, il est bon de rajouter une contrainte vérifiant que la date de fin de congés soit postérieure à celle du début des congés :
ALTER TABLE hotel ADD CONSTRAINT contrainte_posterite CHECK (debutconges <= finconges ); ●
Ajouter une contrainte à cette table hotel empêcher hotel empêcher les congés annuels de dépasser 21 jours. ALTER TABLE hotel ADD CONSTRAINT contrainte_conges CHECK (debutconges-finconges (debutconges-finconges < 21);
●
Modifier en conséquence les données, en prenant pour principe que les hôtels de Nice sont fermés entre le 22/01 et le 10/02, que ceux des Alpes sont fermés du 1/08 au 18/08, et que les autres
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
finconges = TO_DATE('10/02', 'dd/mm'), WHERE numhotel IN (SELECT numhotel FROM hotel WHERE ville='Nice'); UPDATE hotel SET debutconges = TO_DATE('01/08', 'dd/mm'), finconges = TO_DATE('18/08', 'dd/mm'), WHERE numhotel IN (SELECT numhotel FROM hotel WHERE ville='Grenoble'); UPDATE hotel SET debutconges = TO_DATE('01/11', 'dd/mm'), finconges = TO_DATE('21/11', 'dd/mm'), WHERE numhotel NOT IN (SELECT numhotel FROM hotel WHERE ville='Grenoble' ville='Grenoble' UNION SELECT numhotel FROM hotel WHERE ville='Nice'); Question 6.
Créer la table facture constituée des colonnes suivantes : num_facture, num_facture, date_facture, date_facture, num_occupation, num_occupation, montant_ht , payee. payee. Expliquez pourquoi ces données suffisent pour imprimer une facture pour un séjour, séjour, avec nom du client, dates du séjour, etc. Si vous désirez ajouter d'autres colonnes, faites le mais en justifiant votre choix. (1) Pour créer la table, on utilise la commande CREATE TABLE en spécifiant chaque colonne. On réfléchit aux éventuelles contraintes pouvant être posées sur ces colonnes : il y a la contrainte de clé primaire que nous posons naturellement sur num_facture, num_facture, le montant de la facture que nous imposons positif ou nul, la date non vide (et par défaut la date du jour de création de la facture), une contrainte de clé étrangère sur num_occupation qui référence la même colonne -- numoccup -dans la table occupation. occupation. Nous imposons aussi que ce numéro d'occupation soit non vide, et unique car deux factures ne peuvent concerner la même occupation. CREATE TABLE facture ( num_facture NUMBER NOT NULL CONSTRAINT cp_facture cp_facture PRIMARY KEY, date_facture date NOT NULL DEFAULT CURRENT_DATE, num_occupation NUMBER NOT NULL UNIQUE, montant_ht NUMBER CONSTRAINT ch_montant CHECK (montant_ht > 0), payee bool DEFAULT false, CONSTRAINT ce_facture_occup FOREIGN KEY (num_occupation) REFERENCES occupation(numoccup) );
(2) Ces données suffisent car elles permettent de retrouver dans la base toutes les données nécessaires à l'établissement de la facture : le numéro d'occupation permet d'accéder au client (puisque la table occupation référence le client), à l'hôtel (puisque la table occupation référence l'hôtel) et au prix (puisque la table occupation référence référence la chambre, chambre, donc le prix par nuit). Par ailleurs, ailleurs, les dates du séjour sont aussi présentes dans la table occupation. occupation. Donc tout y est ! Pour chaque séjour terminé, créer une ligne de la table facture, avec la date courante comme date d'édition de la facture (le cas du numéro de facture ne sera pas forcément traité). Nous allons allons utili utiliser ser deux fonctio fonctions ns : une première première qui créé une factur facture e à partir partir du numéro numéro d'occupation, une seconde qui génère toutes les factures à l'aide de la table occupation. Un prob problè lème me se pos pose que que nous nous ne géro gérons ns pas pas ici : nor normal malemen ementt nous nous devo devons ns attr attriibuer buer automatiquement à automatiquement à chaque facture un numéro différent. Pour ce faire, il nous faudrait utiliser deux structures que nous ne connaissons pas encore : les triggers et les séquences. Nous décidons donc de “botter en touche” en considérant dans la première fonction que le numéro de la facture à
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
-- nocc: num d'occupation, nfact: num de facture
cout_chambre chambre.prixnuitht%TYPE; une_occup occupation%ROWTYPE; prix_sejour NUMBER(4); la_facture NUMBER; BEGIN
-- le prix de la chambre qui a été occupée -- l'occupation en cours de traitement (1 ligne) -- le prix total du séjour -- numéro de la facture de l'occupation n° nocc -- On vérifie que la facture n'existe pas déjà pour cette occupation
SELECT num_facture INTO la_facture FROM facture WHERE num_occupation = nocc; -- On crée la facture que si elle n'existe pas déjà IF ( la_facture IS NULL ) THEN -- Récupération de l'occupation à partir de son identifiant
SELECT * INTO une_occup FROM occupation WHERE occupation.numoccup = nocc; -- Récupération du prix de la chambre occupée à partir de son identifiant
SELECT prixnuitht INTO cout_chambre FROM chambre WHERE chambre.numchambre = une_occup.numchambre; -- Calcul du prix total HT du séjour
prix_sejour := ( une_occup.datedepart - une_occup.datearrivee ) * cout_chambre ; -- Insertion de la f acture avec les données actuelles
INSERT INTO facture (num_facture, num_occupation, montant_ht) VALUES (nfact, nocc, prix_sejour); la_facture := nfact; END IF; -- Retour du numéro de la facture créée ou récupérée RETURN nfact ; END; CREATE OR REPLACE FUNCTION creations_factures() RETURN NUMBER IS -- Les données de l'occupation en cours de traitement une_occup occupation%ROWTYPE; CURSOR les_occupations IS SELECT * FROM occupation; -- Toutes les occupations -- Le nombre de factures créées nbfact NUMBER := 0; BEGIN -- Récupération de toutes les occupations OPEN les_occupations; -- Début de parcours des occupations FETCH les_occupations INTO une_occup; WHILE les_occupations%FOUND LOOP -- On cherche si l'occupation est valide
IF une_occup.datedepart IS NOT NULL AND une_occup.datedepart <= CURRENT_DATE THEN -- l'occupation est valide : création de la facture DECLARE -- Le n° de facture: ne sert à rien ici... numf NUMBER; -- Appel à la fonction de création individuelle de facture BEGIN numf := creation_facture(une_occup.numoccup, nbfact); -- incrémentation du nb total de factures créées nbfact := nbfact+1; END; END IF; FETCH les_occupations INTO une_occup; -- On passe à l'occupation suivante END LOOP; CLOSE les_occupations; -- Fermeture des occupations: il n'y en a plus -- modification effective de la base COMMIT; RETURN nbfact;
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
CREATE OR REPLACE FUNCTION edition_facture(nfacture NUMBER) RETURN VARCHAR IS DECLARE -- La facture à éditer une_facture facture%ROWTYPE; une_occup occupation%ROWTYPE; -- L'occupation concernée -- Le client concerné un_client client%ROWTYPE; -- L'hôtel concerné un_hotel hotel%ROWTYPE; -- Le numéro de la chambre numch chambre.numchambre%TYPE; -- Le texte final text VARCHAR; paye VARCHAR; -- 'oui' si facture payée, 'non' sinon BEGIN -- Récupération de la facture
SELECT * INTO une_facture FROM facture WHERE facture.num_facture = nfacture; -- Récupération de l'occupation
SELECT * INTO une_occup FROM occupation WHERE une_facture.num_occupation = occupation.numoccup; -- Récupération du client
SELECT * INTO un_client FROM client WHERE client.numclient = une_occup.numclient; -- Récupération de l'hôtel
SELECT * INTO un_hotel FROM hotel WHERE hotel.numhotel = une_occup.numhotel; -- Récupération du numéro de chambre
SELECT * INTO numch FROM chambre WHERE chambre.numchambre = une_occup.numchambre; -- Récupération de l'information sur le paiement
IF une_facture.payee THEN paye = 'oui'; ELSE paye := 'non'; END IF; -- Construction de l'édition : un \n est un retour-chariot, et \t est une tabulation
text := un_hotel.nom + ' à ' + un_hotel.ville + '\n' 'Facture adressée à ' + un_client.nom + ' ' + un_client.prenom + ' (numéro: ' + un_client.numclient + ')\n' + 'Date arrivée: ' + TO_CHAR(une_occup.datearrivee, 'DD-MM-YYYY') + '\n' + 'Date départ : ' + TO_CHAR(une_occup.datedepart, 'DD-MM-YYYY') + '\n' 'N° chambre occupée : ' + numch + '\n' + 'Nombre nuit(s) : ' + une_occup.datedepart-une_occup.datearrivee + '\n' + 'Montant HT = ' + une_facture.montant_ht + '\n' 'Montant TTC = ' + une_facture.montant_ht + une_facture.montant_ht/100*19.6 + '\n' + 'Réglé : ' + paye + '\n \t \t A ' + un_hotel.ville + ' le ' + TO_CHAR(une_facture.date_facture, 'DD-MM-YYYY'); -- Retour du texte édité RETURN text; END;
Question 7.
Ecrire une fonction qui accepte en paramètre le nom et la ville d'un l'hôtel et qui retourne le prix moyen des chambres dans cet hôtel. cet hôtel. Cette question n'est pas plus compliquée que les précédentes à l'exception de l'utilisation d'un curseur avec des paramètres. En effet, il nous faut récupérer toutes les chambres de l'hotel qui
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
somme_prix :=0 ; une_chambre chambre%ROWTYPE; CURSOR les_chambres(n_h VARCHAR, n_v VARCHAR) IS SELECT numchambre, numhotel, etage, type, prixnuitht FROM chambre JOIN hotel USING(numhotel) WHERE hotel.nom = n_h AND hotel.ville = n_v; BEGIN OPEN les_chambres (nom_hotel, ville_hotel); FETCH les_chambres INTO une_chambre; WHILE (les_chambres%FOUND) LOOP nb_chambres := nb_chambres +1; somme_prix := somme_prix + une_chambre.prixnuitht; END LOOP; CLOSE les_chambres; RETURN somme_prix / nb_chambres; END;
Question 8. Créer une fonction de demande de réservation : cette fonction prend en paramètres
la ville du séjour, le nom de l'hôtel, la date de début de séjour et la date de fin, et le type de chambr chambre e à réserv réserver er,, et enfin enfin le numér numéro o de client client.. La foncti fonction on consul consulte te alors alors les chambr chambres es disponibles de ce type, et crée la réservation si c'est possible. Si la réservation a été créée, la fonction retourne le numéro de la commande créée, sinon la fonction renvoie -1. Cette fonction demande un peu de méthode pour être écrite : dans un premier temps, on récupère l'identifiant de l'hôtel concerné (lhotel (lhotel)) à l'aide de son nom et de la ville : on utilise pour cela un SELECT INTO, INTO, et même s'il existe plusieurs hôtels portant le même nom dans la même ville, on ne garde ainsi que le premier retourné. Notons ici que si un tel hôtel n'existe pas, la fonction se conte contente nte de reto retourn urner er -1 (on (on teste teste que le SELECT SELECT INTO INTO renv renvoi oie e un numé numéro ro d'hôte d'hôtell grâc grâce e à l'instruction IF ( lhotel IS NOT NULL ) placée juste après la commande SELECT. SELECT. Une fois l'identifiant de l'hôtel récupéré, on fait appel à une fonction chambres_libres(date_ar chambres_libres(date_arrivee, rivee, date_depart, num_hotel) qui renvoie les numéros des chambres disponibles dans un intervalle de temps donné et dans un hôtel donné. Cette liste de numéros de chambres est stockée dans le curseur ch_libres curseur ch_libres.. On itère alors sur ce curseur, jusqu'à ce qu'il n'y ait plus de chambre libre, ou jusqu'à ce qu'une chambre libre et de type adéquat ait été trouvée trouvée (dans (dans ce dernie dernierr cas, cas, la variab variable le OK prend prend pour valeur valeur l'ident l'identifi ifiant ant de la réserv réservati ation on nouvellement effectuée). Dans cette itération, on récupère le type de la chambre courante : s'il corr corres espo pond nd au type type de cham chambr bre e désir désiré é par par le clie client nt (en (en param paramèt ètre re de la fonct fonctio ion) n),, on crée crée effectivement la réservation et on récupère son identifiant ; sinon, on passe à la chambre libre suivante dans le curseur. A la fin, on retourne la valeur de la variable OK qui vaut -1 si aucune réservation n'a été faite, ou le numéro de la réservation si elle a été possible. Notons que dans cette fonction, nous ne testons rien sur la cohérence des paramètres et de la base, sauf le fait que l'hôtel demandé existe bien. Nous pourrions vérifier que le client existe vraiment, que la date d'arrivée est antérieure à la date de départ, etc. Cela devrait d'ailleurs être fait dans une programmation parfaite. Le problème de gestion du numéro de réservation est le même que celui des numéros de facture, mais il ne peut être gérer aussi facilement que dans la question 6. On se contente ici de faire appelle à une mystérieuse fonction fonction_mystère() fonction_mystère() ;-) CREATE OR REPLACE FUNCTION faire_resa (datea DATE, dated DATE, nclient NUMBER, typch VARCHAR, villeh VARCHAR, nomh VARCHAR)
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
CURSOR ch_libres( da DATE, dd DATE, nh NUMBER) IS SELECT * FROM chambres_libres(da, dd, nh); BEGIN -- Recherche de l'hôtel exact concerné
SELECT numhotel INTO lhotel FROM hotel WHERE ville=villeh AND nom = nomh; -- Nous ne faisons la tentative de réservation que si l'hôtel existe IF (lhotel IS NOT NULL ) THEN -- Recherche d'une chambre libre pendant la période donnée
OPEN ch_libres(datea, dated, lhotel); FETCH ch_libres INTO nchambre;
-- nchambre : numéro de la chambre étudiée
-- tant que l'on n'a pas trouvé de chambre du bon type, on itère -- Pour chaque chambre dispo WHILE ( ch_libres%FOUND AND OK = -1 ) LOOP DECLARE le_type VARCHAR; BEGIN SELECT type INTO le_type FROM chambre WHERE numchambre = nchambre ; -- On observe le type de chambre
IF ( le_type IS NOT NULL AND le_type = typch ) -- La chambre récupérée est du bon type THEN -- On crée la réservation
INSERT INTO reservation (numclient, numchambre, numhotel, datearrivee, datedepart) VALUES ( nclient, nchambre, lhotel, datea, dated); -- On récupère le numéro de la nouvelle réservation
OK := fonction_mystère(); fonction_mystère(); -- la chambre n'est pas du bon type, on passe à la suivante ELSE FETCH ch_libres INTO nchambre; END IF; END; END LOOP; END IF; COMMIT; -- Retour du numéro de la réservation créée, ou -1 si aucune chambre dispo
RETURN OK; END;
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
Titles you can't find anywhere else
Try Scribd FREE for 30 days to access over 125 million titles without ads or interruptions! Start Free Trial Cancel Anytime.
CURSOR chambres_libres(fd DATE, td DATE, nh NUMBER) IS ( SELECT chambre.numchambre FROM ( SELECT hotel.numhotel FROM hotel WHERE hotel.numhotel = nh AND NOT ((conge_debut, conge_fin) OVERLAPS (fd, td)) ) hostel JOIN chambre ON hostel.numhotel = chambre.numhotel ) MINUS ( ( SELECT chambre.numchambre FROM chambre JOIN ( SELECT occupation.numhotel, occupation.numchambre FROM occupation WHERE ( datearrivee >= fd AND datearrivee < td ) OR ( datedepart > fd AND datedepart <= td ) OR ( ( datedepart >= td OR datedepart IS NULL ) AND datearrivee <= fd ) ) ) occupees ON ( nh = occupees.numhotel AND chambre.numchambre = occupees.numchambre ) WHERE chambre.numhotel = nh ) UNION ( SELECT chambre.numchambre FROM chambre JOIN ( SELECT reservation.numhotel, reservation.numchambre FROM reservation WHERE ( datearrivee >= fd AND datearrivee < td ) OR ( datedepart > fd AND datedepart <= td ) OR ( ( datedepart >= td OR datedepart IS NULL ) AND datearrivee <= fd ) ) ) reservees ON ( nh = reservees.numhotel AND chambre.numchambre = reservees.numchambre ) WHERE chambre.numhotel = nh ) ); BEGIN OPEN chambres_libres(fdate, tdate, nhotel); FETCH chambres_libres INTO une_chambre;