©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD UNIVERSITE DE GENEVE FACULTE DES SCIENCES ECONOMIQUES ET SOCIALES Département de Systèmes d'Information TP FINAL LA BIBLIOTHEQUE Part II CORRIGE Date à rendu : le lundi 10 juin 02 Modalité de rendu : par courrier électronique sous forme nom_de_login.TPfinal2.doc (ex. BDD2002_12.TPfinal2.doc) Adresse : [email protected] TP final Corrigé 1 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD La bibliothèque I. La modélisation relationnelle suivante représente la structure statique de la base de données de la bibliothèque. ADHERENT(noAdh// nom, prénom, adresse, dateAdh) CATALOGUE(titre // nomAut, prénomAut, anEd) FONDS(cote // titre) RESERVATION(titre, noAdh, dateRes // dateEmp) EMPRUNT(cote, dateEmp // noAdh, dateRet) II. Les règles d’intégrité : 1. Tous les attributs d’un adhérent ne peuvent prendre des valeurs inconnues. 2. Tous les attributs d’un catalogue ne peuvent prendre des valeurs inconnues. L’an d’édition doit après 1950. 3. Tous les attributs d’un fonds ne peuvent prendre des valeurs inconnues. 4. Toute réservation, la date de réservation doit être précédente de celle d’emprunt. 5. Tout emprunt, la date d’emprunt doit être précédente de celle de retour. 6. On ne peut réserver ou emprunter un titre qu’après avoir adhéré à la bibliothèque. (La date d’adhérent est précédente de celle de réservation ou d’emprunt) 7. Un emprunt ne doit pas dépasser un mois. 8. Un adhérent ne peut emprunter qu’au plus 5 titres à la fois et un seul exemplaire pour chaque titre. 9. Lorsqu’un emprunt est fait à partir d’une réservation, on enregistre la date d’emprunt à la réservation. (La date d’emprunt dans une réservation est claire, alors il existe un emprunt tel que la cote est un exemplaire du titre réservé et l’emprunteur est l’adhérent qui réserve ce titre) Question Part I. =================== Création des tables ======================= CREATE TABLE adherent ( noadh CHAR(5) PRIMARY KEY, nom CHAR(20) NOT NULL, prenom CHAR(30) NOT NULL, adresseVARCHAR(50) NOT NULL, dateadh DATE NOT NULL ); CREATE TABLE catalogue ( titre CHAR(30) PRIMARY KEY, nom CHAR(20) NOT NULL, prenom CHAR(30) NOT NULL, anEd NUMBER(4) NOT NULL CONSTRAINT chk_anEd CHECK (anEd>=1950) ); TP final Corrigé 2 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD CREATE TABLE fonds ( cote CHAR(10) PRIMARY KEY, titre CHAR(30) NOT NULL REFERENCES catalogue ); CREATE TABLE emprunt ( cote CHAR(10), dateemp DATE, noadh CHAR(5) NOT NULL, dateret DATE, CONSTRAINT pk_emprunt PRIMARY KEY(cote, dateemp), CONSTRAINT fk_adherent FOREIGN KEY (noadh) REFERENCES adherent, CONSTRAINT fk_fonds FOREIGN KEY(cote) REFERENCES fonds, CONSTRAINT chk_dateemp CHECK(dateemp<=dateret), CONSTRAINT chk_dateret CHECK(dateret<=add_months(dateemp,1)) ); CREATE TABLE reservation( titre CHAR(30) REFERENCES catalogue, noadh CHAR(5) REFERENCES adherent, dateres DATE, dateemp DATE, CONSTRAINT pk_reservation PRIMARY KEY (titre, noadh, dateres), CONSTRAINT chk_dateres CHECK (dateres <= dateemp) ); ============= INSERTION DES DONNEES ======================= insert into ADHERENT values ('A001','RENAULT','Robert','Lausanne','08/04/01'); insert into ADHERENT values ('A002','VOLVO','Robert','Geneve','01/01/02'); insert into ADHERENT values ('A003','AQUA','Robert','Sion','25/06/01'); insert into CATALOGUE values ('Base de donnees','ADIBA','Michel',1995); insert into CATALOGUE values ('Base des objets','COLLAND','Rollet',1990); insert into CATALOGUE values ('OO DMBS','ADIBA','Michel',1998); insert into CATALOGUE values ('ORACLE SQL','LINDEN','Brian',2000); insert into CATALOGUE values ('SQL*Plus reference','LINDEN','Brian',2001); insert into CATALOGUE values ('Web Database','BUYENS','Jim',2000); insert into FONDS values('BDD_01','Base de donnees'); insert into FONDS values('BDD_02','Base de donnees'); insert into FONDS values('BD0_01','Base des objets'); insert into FONDS values('OOMS_01','OO DMBS'); insert into FONDS values('OOMS_02','OO DMBS'); insert into FONDS values('ORA.SQL_01','ORACLE SQL'); insert into FONDS values('SQLP_01','SQL*Plus reference'); insert into FONDS values('WDB_00','Web Database'); TP final Corrigé 3 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD insert into RESERVATION values('Base de donnees','A001','09/04/01',NULL); insert into RESERVATION values('Base de donnees','A002','10/01/02', NULL); insert into RESERVATION values('OO DMBS','A002','02/01/02',NULL); insert into EMPRUNT values('BDD_02','21/04/01','A001', '03/05/01'); insert into EMPRUNT values('BDD_01','01/06/02','A001', NULL); insert into EMPRUNT values('BD0_01','03/06/02','A001', NULL); insert into EMPRUNT values('OOMS_02','03/06/02','A001', NULL); insert into EMPRUNT values('ORA.SQL_01','01/06/02','A001', NULL); insert into EMPRUNT values('SQLP_01','01/06/02','A001', NULL); insert into EMPRUNT values('OOMS_02','05/06/02','A002', NULL); insert into EMPRUNT values('OOMS_01','05/01/02','A002', '10/01/02'); update RESERVATION set dateEmp='21/04/01' where titre='Base de donnees' and noAdh='A001' and dateRes='09/04/01'; update RESERVATION set dateEmp='05/01/02' where titre='OO DMBS' and noAdh='A002' and dateRes='02/01/02'; TP final Corrigé 4 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD Part II. RI6 : Inter-relations : ADHERENT, RESERVATION, EMPRUNT ADHERENT : i. Modifier la date d’adhésion (dateAdh) : Refuser la modification et afficher une erreur s’il existe déjà des réservations ou des emprunts de cet adhérent dont la date est antérieure à la nouvelle date. RESERVATION : i. Créer une réservation : Si la date de réservation (dateRes) est antérieure la date d’adhésion (dateAdh), affecter dateAdh à dateRes. EMPRUNT i. Créer un emprunt : Si la date d’emprunt (dateEmp) est antérieure la date d’adhésion (dateAdh), affecter dateAdh à dateRes. ii. Modifier le numéro d’adhérent : Refuser la modification et afficher une erreur si le nouveau numéro d’adhérent ne satisfait pas la règle. RI8 : RI8.1. Un adhérent ne peut emprunter qu’au plus 5 exemplaires à la fois Intra-relation (EMPRUNT), Ensembliste EMPRUNT : i. Créer un emprunt : Refuser cette insertion et afficher une erreur si le nombre des emprunts qui ne sont pas encore rendus est déjà 5. ii. Modifier la date de retour : Refuser la modification et afficher une erreur si elle transgresse la règle. RI8.2. un seul exemplaire pour chaque titre Inter-relations (EMPRUNT, FONDS) EMPRUNT : i. Créer un emprunt : Refuser cette insertion et afficher une erreur si cet adhérent a emprunté (pas encore rendu) un exemplaire du même titre. ii. Modifier la date de retour : Refuser la modification et afficher une erreur si elle transgresse la règle. FONDS : i. Modifier le titre : Refuser la modification et afficher une erreur s’il existe déjà des emprunts de cet exemplaire. RI9 : Inter-relations (FONDS, RESERVATION, EMPRUNT) FONDS : i. Modifier le titre : Refuser la modification et afficher une erreur s’il existe déjà des emprunts de cet exemplaire. ii. Supprimer un fonds : Refuser la suppression. (Cette opération est déjà vérifiée grâce à la clé étrangère) RESERVATION : i. Créer une réservation : la date d’emprunt doit être inconnue lorsqu’une nouvelle réservation est faite (insérée). ii. Modification la date d’emprunt : refuser la modification et afficher une erreur si elle transgresse la règle. EMPRUNT : i. Modifier l’adhérent : Refuser la modification et afficher une erreur si elle transgresse la règle. ii. Supprimer un emprunt : Si cet emprunt concerne une réservation, refuser la suppression et afficher une erreur. TP final Corrigé 5 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD 1. Tableau de portée global Table ADHERENT Opération RI6 MAJ dateAdh FONDS MAJ RESERVATION INS MAJ EMPRUNT INS MAJ SUP RI8.1 RI8.2 titre + + noAdh RI9 titre + dateEmp + dateRet + dateRet noAdh + 2. Implémentation des RI 2.1. ADHERENT CREATE OR REPLACE TRIGGER check_dateAdh AFTER UPDATE OF dateAdh ON ADHERENT FOR EACH ROW WHEN (NEW.dateAdh > OLD.dateAdh) DECLARE cnt_Emp NUMBER(3); cnt_Res NUMBER(3); BEGIN /* Vérifier si la nouvelle date d'adhésion est postérieure à la date des emprunts*/ SELECT COUNT(*) INTO cnt_Emp FROM EMPRUNT WHERE noAdh = :new.noAdh and dateEmp<=:new.dateAdh; /* Si oui, refuser la MAJ. RI6. */ IF (cnt_Emp>0) THEN RAISE_APPLICATION_ERROR(-20050,'MAJ de date d''adhésion est non-valide'); END IF; /* Vérifier si la nouvelle date d'adhésion est postérieure à la date des réservations*/ SELECT COUNT(*) INTO cnt_Res FROM RESERVATION WHERE noAdh = :NEW.noAdh and dateRes<=:NEW.dateADh; /* Si oui, refuser la MAJ. RI6 */ IF (cnt_Res>0) THEN RAISE_APPLICATION_ERROR(-20050,'MAJ de date d''adhésion est non-valide'); END IF; END; TP final Corrigé 6 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD 2.2. FONDS CREATE OR REPLACE TRIGGER maj_fonds AFTER UPDATE OF titre ON fonds FOR EACH ROW DECLARE nb_emp NUMBER(5); BEGIN /* Vérifier si ce fonds actuel est déjà emprunté */ SELECT COUNT(*) INTO nb_emp FROM emprunt WHERE cote = :OLD.cote; /* Si oui, réfuser la MAJ. RI9 et RI8.2 */ IF nb_emp>0 THEN RAISE_APPLICATION_ERROR(-20052,'Ce fonds a des emprunts. La MAJ est interdite'); END IF; END; 2.3. RESERVATION CREATE OR REPLACE TRIGGER cre_reservation BEFORE INSERT ON reservation FOR EACH ROW DECLARE date_Adh ADHERENT.DATEADH%TYPE; BEGIN /* Chercher la date d'adhésion de la personne qui fait une nouvelle réservation.*/ SELECT dateAdh INTO date_Adh FROM adherent WHERE noAdh = :NEW.noAdh; /* Si la date d'adhésion est postérieure à celle de réservation, affecter la date d'adhésion actuelle à la date de réservation. RI6*/ IF date_Adh>:NEW.dateRes THEN :NEW.dateRes := date_Adh; END IF; /* Si la date d'emprunt de la nouvelle réservation est connue (not null), affecter la valeur inconnue à la place. RI9 */ IF (:NEW.dateEmp is NOT NULL) THEN :NEW.dateEmp := NULL; END IF; END; TP final Corrigé 7 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD CREATE OR REPLACE TRIGGER maj_reservation AFTER UPDATE OF dateEmp ON reservation FOR EACH ROW WHEN (NEW.dateEmp IS NOT NULL) DECLARE nb_emp NUMBER(1); BEGIN /* Vérifier s'il existe un emprunt correspondant à la "nouvelle" réservation*/ SELECT COUNT(*) INTO nb_emp FROM emprunt, fonds WHERE emprunt.cote=fonds.cote AND noAdh=:NEW.noAdh AND titre=:NEW.titre AND dateEmp=:NEW.dateEmp; /* Si non, réfuser la MAJ. RI9 */ IF nb_emp=0 THEN RAISE_APPLICATION_ERROR(-20054,'Il n''y pas d''emprunt correspondant à cette réseravation'); END IF; END; 2.4. EMPRUNT CREATE OR REPLACE TRIGGER avant_cre_emprunt BEFORE INSERT ON emprunt FOR EACH ROW DECLARE dat_Adh ADHERENT.DATEADH%TYPE; BEGIN /* Chercher la date d'adhésion de la personne qui fait le nouvel emprunt.*/ SELECT dateAdh INTO dat_Adh FROM adherent WHERE noAdh = :new.noAdh; /* Si la date d'adhésion est postérieure à celle d'emprunt, affecter la date d'adhésion actuelle à la place. RI6 */ IF dat_Adh>:new.dateEmp THEN :new.dateEmp := dat_Adh; END IF; END; TP final Corrigé 8 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD CREATE OR REPLACE TRIGGER cre_emprunt AFTER INSERT OR UPDATE OF dateRet ON EMPRUNT DECLARE cntdat_Adh number; maxnb_Emp number; maxnb_Exemplaire number; BEGIN /* Chercher la quantité maximale des livres empruntés par l'adhérent */ SELECT MAX(COUNT(*)) INTO maxnb_Emp FROM emprunt WHERE dateRet is null GROUP BY noAdh; /* S'il existe un adhérent qui emprunte plus 5 livres, la RI8.1 est trangressée, l'insertion sera interdite */ IF maxnb_Emp > 5 THEN RAISE_APPLICATION_ERROR(-20056,'Cet adhérent a déjà emprunté 5 livres'); END IF; /* Chercher la quantité maximale des exemplaires des oeuvres empruntés par l'adhérent */ SELECT MAX(COUNT(*)) INTO maxnb_Exemplaire FROM emprunt, fonds WHERE (emprunt.cote=fonds.cote) and (dateRet is null) GROUP BY noAdh, titre; /* S'il existe un titre qui est emprunté deux fois, l'insertion est interdite. RI8.2 */ IF maxnb_Exemplaire > 1 THEN RAISE_APPLICATION_ERROR(-20057,'Ce titre est déjà emprunté'); END IF; END; CREATE OR REPLACE TRIGGER sup_emprunt AFTER DELETE ON emprunt FOR EACH ROW DECLARE v_titre FONDS.TITRE%TYPE; nb_res NUMBER(1); BEGIN /* Chercher le titre de l'emprunt supprimé */ SELECT titre INTO v_titre FROM fonds WHERE cote = :OLD.cote; /* Vérifier si cet emprunt concerne à une réservation */ SELECT COUNT(*) INTO nb_res FROM reservation WHERE titre=v_titre AND noAdh=:OLD.noAdh AND dateEmp=:OLD.dateEmp; /* Si oui, interdire la MAJ. RI9 */ IF nb_res>0 THEN RAISE_APPLICATION_ERROR(-20058,'Cet emprunt concerne à une réservation. La suppression est interdite'); END IF; END; TP final Corrigé 9 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD CREATE OR REPLACE TRIGGER maj_noAdh_emprunt AFTER UPDATE OF noAdh ON emprunt FOR EACH ROW DECLARE date_Adh ADHERENT.DATEADH%TYPE; nb_res NUMBER(1); v_titre FONDS.TITRE%TYPE; BEGIN /* Chercher la date d'adhésion de la personne qui fait le "nouvel" emprunt.*/ SELECT dateAdh INTO date_Adh FROM adherent WHERE noAdh = :new.noAdh; /* Si la date d'adhésion est postérieure à celle d'emprunt, réfuser la MAJ. RI6 */ IF date_Adh>:new.dateEmp THEN RAISE_APPLICATION_ERROR(-20051,'MAJ du numéro d''adhérent dans l''emprunt est non-valide'); END IF; /* Chercher le titre de l'emprunt actuel (ancien) */ SELECT titre INTO v_titre FROM fonds WHERE cote = :OLD.cote; /* Vérifier si l'emprunt actuel concerne à une réservation */ SELECT COUNT(*) INTO nb_res FROM reservation WHERE titre=v_titre AND noAdh=:OLD.noAdh AND dateEmp=:OLD.dateEmp; /* Si oui, interdire la MAJ. RI9 */ IF nb_res>0 THEN RAISE_APPLICATION_ERROR(-20055,'Cet emprunt concerne à une réservation. La MAJ est interdite'); END IF; END; 3. Tester les triggers RI.2. INSERT INTO catalogue VALUES ('Constraint database','REVESZ','Peter', 1949); RI.4. INSERT INTO reservation VALUES('SQL*Plus reference','A002','28/06/01','26/06/01'); RI.5. INSERT INTO emprunt VALUES('SQLP_01','27/05/02','A003','20/05/02'); RI.6. ADHERENT : maj dateAdh UPDATE adherent SET dateAdh='10/04/01' WHERE noAdh ='A001'; RESERVATION : créer INSERT INTO reservation VALUES('SQL*Plus reference','A001','08/03/01',NULL); EMPRUNT : créer INSERT INTO emprunt VALUES('SQLP_01','24/06/01','A003',NULL); EMPRUNT : maj noAdh UPDATE emprunt SET noAdh='A003' WHERE noAdh='A001' AND cote='BDD_02'; TP final Corrigé 10 ©M. Léonard, H. Luu Université de Genève - SES Été 2002 Bases de données - BDD RI.7. INSERT INTO emprunt VALUES('SQLP_01','27/04/02','A003','28/05/02'); RI.8.1. EMPRUNT : créer INSERT INTO emprunt VALUES('WDB_00','10/06/02', 'A001', NULL); EMPRUNT : maj dateRet UPDATE emprunt SET dateRet=NULL WHERE cote='BDD_02' AND noAdh='A001'; RI.8.2. EMPRUNT : créer INSERT INTO emprunt VALUES('OOMS_01','10/06/02', 'A002', NULL); EMPRUNT : maj dateRet UPDATE emprunt SET dateRet=NULL WHERE cote='OOMS_01' AND noAdh='A002'; FONDS : maj titre UPDATE fonds SET titre='Base de donnees 1' WHERE titre='Base de donnees'; RI.9. FONDS : maj titre UPDATE fonds SET titre='Base de donnees 1' WHERE titre='Base de donnees'; RESERVATION : créer INSERT INTO reservation VALUES('SQL*Plus reference','A002','28/06/01','30/06/01'); RESERVATION : maj dateEmp UPDATE reservation SET dateEmp='30/06/01' WHERE titre='SQL*Plus reference' AND noAdh='A002'; EMPRUNT : maj noAdh UPDATE emprunt SET noAdh='A003' WHERE cote='OOMS_01' AND dateEmp='05/01/02'; EMPRUNT : supprimer DELETE FROM emprunt WHERE cote='OOMS_01' AND dateEmp='05/01/02'; TP final Corrigé 11