Vous êtes sur la page 1sur 15

Institut Supérieur d'Informatique et de Mathématiques de Monastir

Correction SERIE DE TD N° 4
Langage PL/SQL

Sections : LF2 et LA2 Informatique Matière : SGBD

Exercice 1 :

Soit le schéma relationnel suivant :


EMP (Matr, NomE, Poste, DateEmb, Sup#, Salaire, Comm, NumDept#)
DEPT (NumDept, NomDept, Lieu, directeur#)
PROJET (CodeP, NomP)
PARTICIPATION (Matr#, CodeP#, Fonction)
Répondez aux questions suivantes :
1. Définir un bloc PL/SQL anonyme, en prévoyant les exceptions que vous jugez nécessaire,
permettant de lire les détails des projets effectués par tous les employés, le résultat est trié
selon la matricule employé.

Réponse :

Set serveroutput on ;
DECLARE
Cursor C_emp is select P.Matr, P.CodeP, Pr.NomP from Participation P, Projet Pr
Where P.CodeP = Pr.CodeP order by P.Matr;
E_emp Exception;
rec C_emp%rowtype;
BEGIN
Open C_emp;
DBMS_output.put_line(RPAD('Matr.Emp.',15) || RPAD('Code Projet',15) || RPAD('Nom
Projet',15));
loop
Fetch C_emp into rec;
exit when C_emp%notFound;
DBMS_output.put_line(RPAD(rec.Matr,15) || RPAD(rec.CodeP,15) ||
RPAD(rec.NomP,15));
end loop;
if(C_emp%rowcount = 0) then
raise E_emp;

Page 1/15
end if;
close C_emp;
EXCEPTION
when E_emp then
DBMS_output.put_line('Aucun Projet trouvé...');
--when others then
--DBMS_output.put_line('Exception non reconnue...');
END;
/
2. Définir un bloc PL/SQL anonyme, en prévoyant les exceptions que vous jugez nécessaire,
permettant de lire les détails de tous les employés ainsi que le nombre de projets effectués
par chacun d’eux.

a. En utilisant deux curseurs explicites.


Réponse :

Set serveroutput on ;
DECLARE
Cursor C_emp is select E.Matr, E.NomE, E.Poste, E.NumDept from EMP E order
by E.Matr;
Cursor C_NBProjets(P_Matr EMP.MATR%type) is select count(P.CodeP) from
Participation P, EMP E
Where P.Matr = E.Matr and E.Matr = P_Matr group by P.Matr;
E_emp Exception;
rec C_emp%rowtype;
nbPr Number :=0;
BEGIN
Open C_emp;
DBMS_output.put_line(RPAD('Matr Emp',15) || RPAD('Nom EMP',15) ||
RPAD('Poste EMP',15) || RPAD('Dept EMP',15) || RPAD('nb Projets',15));
loop
Fetch C_emp into rec;
exit when C_emp%notFound;
Open C_NBProjets(rec.Matr);
nbPr:=0;
Fetch C_NBProjets into nbPr;
DBMS_output.put_line(RPAD(rec.Matr,15) || RPAD(rec.NomE,15) ||
RPAD(rec.Poste,15) || RPAD(rec.NumDept,15) || RPAD(nbPr,15));
close C_NBProjets;
end loop;
if(C_emp%rowcount = 0) then
raise E_emp;
end if;
close C_emp;
EXCEPTION
when E_emp then
DBMS_output.put_line('Aucun Employé trouvé...');

Page 2/15
--when others then
--DBMS_output.put_line('Exception non reconnue...');
END;
/
b. En utilisant un curseur explicite et un curseur implicite.
Réponse :

Set serveroutput on ;
DECLARE
Cursor C_emp is select E.Matr, E.NomE, E.Poste, E.NumDept from EMP E order
by E.Matr;
E_emp Exception;
rec C_emp%rowtype;
nbPr Number :=0;
BEGIN
Open C_emp;
DBMS_output.put_line(RPAD('Matr Emp',15) || RPAD('Nom EMP',15) ||
RPAD('Poste EMP',15) || RPAD('Dept EMP',15) || RPAD('nb Projets',15));
loop
Fetch C_emp into rec;
exit when C_emp%notFound;
nbPr:=0;
begin
select count(P.CodeP) into nbPr from Participation P
Where P.Matr = rec.Matr group by P.Matr;
exception
when no_data_found then
nbPr:=0;
end;
DBMS_output.put_line(RPAD(rec.Matr,15) || RPAD(rec.NomE,15) ||
RPAD(rec.Poste,15) || RPAD(rec.NumDept,15) || RPAD(nbPr,15));
end loop;
if(C_emp%rowcount = 0) then
raise E_emp;
end if;
close C_emp;
EXCEPTION
when E_emp then
DBMS_output.put_line('Aucun Employé trouvé...');
when too_many_rows then
DBMS_output.put_line('Commande Select retourne plusieurs lignes...');
END;
/
3. Soit une fonction max_sal_dept(p_deptno emp.deptno%TYPE) qui retourne le salaire
le plus élevé des employés d'un département donné dont le numéro est fourni comme
paramètre :
a. Ecrire cette fonction sous forme d’une fonction non stockée dans un bloc
anonyme.
Réponse :

Page 3/15
Set serveroutput on ;
DECLARE
SalaireMax Number :=0;
function max_sal_dept(p_deptno in emp.numdept%TYPE) return Number is
Cursor C_salaire is select max(salaire) from EMP where numdept = p_deptno;
E_salaire Exception;
MaxSal Number :=0;
BEGIN
Open C_salaire;
Fetch C_salaire into MaxSal;
if(C_salaire%rowcount = 0) then
raise E_salaire;
end if;
close C_salaire;
return MaxSal;
EXCEPTION
when E_salaire then
DBMS_output.put_line('Aucun Employé trouvé...');
return NULL;
END;
BEGIN
SalaireMax:= max_sal_dept(10);
DBMS_output.put_line(RPAD('Le Salaire Maximal est : ',30) || SalaireMax);
END;
/
b. Ecrire cette fonction sous forme d’une fonction stockée.
Réponse :

Create or replace function max_sal_dept(p_deptno in emp.numdept%TYPE)


return Number is
Cursor C_salaire is select max(salaire) from EMP where numdept = p_deptno;
E_salaire Exception;
MaxSal Number :=0;
BEGIN
Open C_salaire;
Fetch C_salaire into MaxSal;
if(C_salaire%rowcount = 0) then
raise E_salaire;
end if;
close C_salaire;
return MaxSal;
EXCEPTION
when E_salaire then
DBMS_output.put_line('Aucun Employé trouvé...');
return NULL;
END max_sal_dept;
/
c. Appeler cette fonction dans les deux cas. Quelle est la différence entre les deux
cas?
Réponse :

Page 4/15
La fonction non stockée est définie est appelée dans le même bloc anonyme (ou autre
bloc nommé), alors elle n’est pas visible ailleurs. Cependant, la fonction stockée est
définie d’une manière indépendante, c’est un bloc nommé indépendant. Elle est
appelée dans un autre bloc nommé/anonyme. Voilà un exemple :

Set serveroutput on ;
DECLARE
SalaireMax Number :=0;
BEGIN
SalaireMax:= max_sal_dept(10);
DBMS_output.put_line(RPAD('Le Salaire Maximal est : ',30) || SalaireMax);
END;
/
4. Dans un bloc PL/SQL anonyme, déclarer et tester une procédure « ListeProjets1 » qui
affiche la liste des projets effectués par tous les employés.

Réponse :

Set serveroutput on ;
DECLARE
procedure ListeProjets1 is
Cursor C_Projets is select distinct P.CodeP, NomP from Participation P, Projet Pr
where P.CodeP = Pr.CodeP order by P.CodeP;
E_Projets Exception;
rec C_Projets%rowtype;
BEGIN
Open C_Projets;
DBMS_output.put_line(RPAD('Code Projet',20) || RPAD('Nom Projet',20));
Loop
Fetch C_Projets into rec;
exit when C_Projets%notfound;
DBMS_output.put_line(RPAD(rec.Codep,20) || RPAD(rec.NomP,20));
end Loop;
if(C_Projets%rowcount = 0) then
raise E_Projets;
end if;
close C_Projets;
EXCEPTION
when E_Projets then
DBMS_output.put_line('Aucun Projet trouvé...');
END;
BEGIN

Page 5/15
-- appel de la procédure :
ListeProjets1;
END;
/
5. Dans un package nommé « Package1 », définir une procédure nommée « ListeProjets2 »
qui affiche la liste des projets qui ne sont pas effectués par les employés de département
20.

6. Dans le package « Package1 », définir une deuxième procédure nommée « ListeProjets3 »


qui affiche la liste des projets qui ne sont pas effectués par aucun employé, en stockant le
résultat dans un paramètre de type OUT.

7. Dans le package « Package1 », définir une fonction nommée « ListeEmployes » qui


retourne les détails (matricule et nom) des employés d’un département dont son
numéro est passé en argument.

Réponse : relative aux questions 5, 6 et 7:

-- définition de l'entête du package :


create or replace package Package1 is
type rec is record (num number, nom varchar2(30));
type tab is table of rec index by binary_integer;
procedure listeProjets2;
procedure ListeProjets3(t out tab);
function ListeEmployes(P_NumDept dept.NumDept%type) return tab;
end Package1;
/
-- définition du corps du package :
create or replace package body Package1 is
-- implémentation de la Procedure listeProjets2
-- Cette procédure affiche les détails des projets qui n'ont pas été effectués par les
employés de departement numéro 20.
procedure listeProjets2
is
cursor C_Projets is select distinct p.codeP,p.nomP
from projet p,participation par
where p.codeP=par.codeP and par.matr not in ( select distinct matr from emp where
numdept=20) order by p.codeP;
rec C_Projets%rowtype;

Page 6/15
E_Projets Exception;
begin
dbms_output.put_line(Rpad('Code Projet',20)|| Rpad('Nom Projet',20));
open C_Projets;
loop
fetch C_Projets into rec;
exit when C_Projets%notfound;
if(C_Projets%rowcount = 0) then
raise E_Projets;
end if;
dbms_output.put_line(Rpad(rec.codeP,20)|| Rpad(rec.NomP,20));
end loop;
close C_Projets;
exception
when E_Projets then
DBMS_output.put_line('Aucun Projet trouvé...');
end listeProjets2;

-- implémentation de la Procedure listeProjets3


-- Cette procédure affiche les détails des projets qui n'ont pas été effectués par aucun
employé en utilisant un paramètre de type OUT.
procedure listeProjets3(t out tab)
is
cursor C_Projets is select codeP,nomP
from projet where CodeP not in (select distinct CodeP from participation) order by codeP;
rec1 C_Projets%rowtype;
E_Projets Exception;
i integer:=0;
begin
open C_Projets;
loop
fetch C_Projets into rec1;
exit when C_Projets%notfound;
if(C_Projets%rowcount = 0) then
raise E_Projets;
end if;
t(i).num:=rec1.codeP;

Page 7/15
t(i).nom:=rec1.nomP;
i:=i+1;
end loop;
close C_Projets;
exception
when E_Projets then
DBMS_output.put_line('Aucun Projet trouvé...');
end listeProjets3;

-- implémentation de la fonction ListeEmployes


-- Cette fonction affiche les détails des employés (matr, nome) d'un département dont son
numéro est passé en paramètre.
function ListeEmployes(P_NumDept IN dept.NumDept%type) return tab
is
cursor C_Emp is select matr,nome
from emp where numdept=p_numdept order by matr;
rec1 C_Emp%rowtype;
E_Emp Exception;
i integer:=0;
t tab;
begin
open C_Emp;
loop
fetch C_Emp into rec1;
exit when C_Emp%notfound;
if(C_Emp%rowcount = 0) then
raise E_Emp;
end if;
t(i).num:=rec1.matr;
t(i).nom:=rec1.nome;
i:=i+1;
end loop;
close C_Emp;
return t;
exception
when E_Emp then
DBMS_output.put_line('Aucun Employé trouvé...');

Page 8/15
return t;
end ListeEmployes;
end Package1;
/

-- Appel des procédures et des fonctions du Package1 :


set serveroutput on
declare
t Package1.tab;
i integer:=-1;
E_NoData exception;
begin
/*
dbms_output.put_line('Appel de la procédure listeProjets2 : ');
Package1.listeProjets2();
*/

/*
dbms_output.put_line('Appel de la procédure listeProjets3 : ');
Package1.listeProjets3(t);
if t.count = 0 then
raise E_NoData;
end if;
dbms_output.put_line(Rpad('Code Projet',30)|| Rpad('Nom Projet',30));
for i in t.first..t.last
loop
dbms_output.put_line(Rpad(t(i).num,30)|| Rpad(t(i).nom,30));
end loop;
exception
when E_NoData then
dbms_output.put_line('Aucun élément trouvé');
*/

/*
dbms_output.put_line('Appel de la fonction ListeEmployes : ');
t := Package1.ListeEmployes(10);
if t.count = 0 then

Page 9/15
raise E_NoData;
end if;
dbms_output.put_line(Rpad('Code Projet',30)|| Rpad('Nom Projet',30));
for i in t.first..t.last
loop
dbms_output.put_line(Rpad(t(i).num,30)|| Rpad(t(i).nom,30));
end loop;
exception
when E_NoData then
dbms_output.put_line('Aucun élément trouvé');
*/
end;
/

Page 10/15
Exercice 2 :

Soit le schéma relationnel suivant :


EMP (Matr, NomE, Poste, DateEmb, Sup#, Salaire, Comm, NumDept#)
DEPT (NumDept, NomDept, Lieu, directeur#)
PROJET (CodeP, NomP)
PARTICIPATION (Matr#, CodeP#, Fonction)
Répondez aux questions suivantes :
1. Dans un bloc PL/SQL anonyme, déclarer et tester une fonction « ListeProjets4 » qui
affiche la liste des projets effectués par tous les employés, en utilisant un curseur explicite
comme type de retour.

Réponse :

Set serveroutput on ;
DECLARE
TYPE refCursor IS REF CURSOR;
C_Projets refCursor;
rec projet%rowtype;
E_Projets Exception;
function ListeProjets1 return refCursor is
resultat refCursor;
BEGIN
OPEN Resultat FOR
select distinct P.CodeP, NomP from Participation P, Projet Pr
where P.CodeP = Pr.CodeP order by P.CodeP;
return Resultat;
end;
BEGIN
-- appel de la fonction :
C_Projets := ListeProjets1;
--Open C_Projets;
DBMS_output.put_line(RPAD('Code Projet',20) || RPAD('Nom Projet',20));
Loop
Fetch C_Projets into rec;
exit when C_Projets%notfound;
DBMS_output.put_line(RPAD(rec.Codep,20) || RPAD(rec.NomP,20));

Page 11/15
end Loop;
if(C_Projets%rowcount = 0) then
raise E_Projets;
end if;
close C_Projets;
EXCEPTION
when E_Projets then
DBMS_output.put_line('Aucun Projet trouvé...');
END;
/
2. Définir un déclencheur (Trigger) « Department_Inserted » qui, après toute adjonction d’un
département, affiche le numéro de département à ajouter.

Réponse :

-- déclencheur pemettant d'afficher un msg après l'adjonction d'un département:


-- ne pas oublier d'activer l'affichage pour tester ce déclencheur
-- l'instruction qui active l'affichage : set serveroutput on;
-- l'instruction qui stimule ce déclencheur : insert into dept values (...);
create or replace trigger Department_Inserted
after insert on Dept
For each row
Begin
DBMS_output.put_line('Le departement ajouté est : ' );
DBMS_output.put_line(RPAD(:New.numdept,20) || RPAD(:New.NomDept,20));
END;
/
3. Définir un déclencheur (Trigger) « Director_Updated » qui, avant toute modification du
directeur d’un département, affiche l’ancien directeur et le nouveau directeur.

Réponse :

-- déclencheur pemettant de modifier le directeur d'un département:


-- ne pas oublier d'activer l'affichage pour tester ce déclencheur
-- l'instruction qui active l'affichage : set serveroutput on;
-- l'instruction qui stimule ce déclencheur : update dept set directeur =x where numdept=y;
create or replace trigger Director_Updated
before update of directeur on Dept
For each row

Page 12/15
Begin
DBMS_output.put_line('Le departement modifié est : ' );
DBMS_output.put_line(RPAD('Ancien directeur',20) || RPAD('Nouveau directeur',20));
DBMS_output.put_line(RPAD(:Old.directeur,20) || RPAD(:New.directeur,20));
END;
/
4. Définir un déclencheur (Trigger) « Department_Deleted » qui, avant toute suppression
d’un département, affiche le numéro de département à supprimer.

Réponse :

-- déclencheur pemettant d'afficher le numéro d'un département à supprimer :


-- ne pas oublier d'activer l'affichage pour tester ce déclencheur
-- l'instruction qui active l'affichage : set serveroutput on;
-- l'instruction qui stimule ce déclencheur : delete from dept where numdept=y;
create or replace trigger Department_Deleted
before delete on Dept
For each row
Begin
DBMS_output.put_line('Le département supprimé est : ' );
DBMS_output.put_line(RPAD('Numéro departement',20));
DBMS_output.put_line(RPAD(:Old.Numdept,20));
END;
/
5. Définir un déclencheur (Trigger) BEFORE «Emp_Control » permettant d’empêcher la
modification (INSERT, UPDATE, DELETE) sur la table EMP en lançant une erreur via la
procédure RAISE_APPLICATION_ERROR.

Réponse :

-- déclencheur permettant d’empêcher la modification (INSERT, UPDATE, DELETE)


--sur la table EMP en lançant une erreur via la procédure
RAISE_APPLICATION_ERROR.
-- pour tester ce déclencheur, il faut essayer d'ajouter une ligne dans la table participation
-- exemple : insert into participation values (...);
-- et ne pas oublier d'activer l'affichage pour tester ce déclencheur
-- l'instruction qui active l'affichage : set serveroutput on;
-- le choix de chaine 'MAR.' ou 'MER.' suivant la langue du système d'exploitation (fr ou
ang).
create or replace trigger emp_control

Page 13/15
before insert or delete or update on Participation
Declare
NoAcces_On_Weekends Exception;
Begin
If ( TO_char(sysdate,'DY') = 'MAR.' OR TO_char(sysdate,'') = 'MER.') then
RAISE NoAcces_On_Weekends;
end if;
Exception
When NoAcces_On_Weekends then
RAISE_APPLICATION_ERROR(-20324,'Vous ne pouvez pas modifier les données des
Participation pendant le Weekend.');
END;
/
6. Désactiver les déclencheurs crées puis les réactiver une autre fois.

Réponse :

-- instruction permettant de désactiver et d'activer un déclencheur:


alter trigger emp_control disable;
alter trigger emp_control enable;

7. Définir un déclencheur (Trigger) BEFORE « Salary_Updated » permettant d’afficher,


avant toute modification du salaire d’un employé, l’ancien salaire et le nouveau salaire si
ce dernier est supérieur à 1000.

Réponse :

-- déclencheur permettant d’afficher, pour toute modification du salaire d’un employé,


-- l’ancien salaire et le nouveau salaire si ce dernier est supérieur à 1000.
-- pour tester ce déclencheur, il faut modifier le salaire d'une ligne dans la table emp
-- exemple : update emp set salaire = 1100 where matr=100;
-- et ne pas oublier d'activer l'affichage pour tester ce déclencheur
-- l'instruction qui active l'affichage : set serveroutput on;
create or replace trigger Salary_Updated
before update of salaire on EMP
for each row
when(New.salaire >1000)
Begin
DBMS_output.put_line('La modification d''un salaire : ' );

Page 14/15
DBMS_output.put_line(RPAD('Ancien Salaire',20) || RPAD('Nouveau Salaire',20));
DBMS_output.put_line(RPAD(:Old.Salaire,20) || RPAD(:New.Salaire,20));
END;
/
8. Définir un déclencheur (Trigger) BEFORE « Salary_ERROR » permettant d’afficher,
avant toute modification du salaire d’un employé, un message d’erreur si le nouveau
salaire est inférieur à 500.

Réponse :

-- déclencheur permettant d’afficher, avant toute modification du salaire d’un employé,


-- un message d’erreur si le nouveau salaire est inférieur à 500.
-- pour tester ce déclencheur, il faut modifier le salaire d'une ligne dans la table emp
-- exemple : update emp set salaire = 400 where matr=100;
-- et ne pas oublier d'activer l'affichage pour tester ce déclencheur
-- l'instruction qui active l'affichage : set serveroutput on;
create or replace trigger Salary_ERROR
before update of salaire on EMP
for each row
Declare
NoUpdate_On_Salary Exception;
Begin
if(:New.salaire < 500) then
RAISE NoUpdate_On_Salary;
end if;
Exception
When NoUpdate_On_Salary then
RAISE_APPLICATION_ERROR(-20324,' Vous ne pouvez pas initialisé le salaire avec
une valeur inférieur à 500.');
END;
/

Page 15/15

Vous aimerez peut-être aussi