Cours
Cours
Cours
Sommaire
1. Programme Bonjour
2. Variables et requête mono-tuple
3. Variables et requête mono-tuple. Critère de recherche saisi à l'exécution
4. Même exemple avec &num utilisée dans la zone declare
5. Recherche avec Salaire, non forcément unique
6. Rajout de zone d'exception
7. Variable de type schéma de table
8. Requête select nécessitant un cursor
1. Exemple simple: on affiche les noms et salaires
2. Autre exemple, calcul du salaire maximum
3. Idem avec while
4. Cursor paramétré
5. Itérateur Abstrait FOR
6. Forme sans déclarer cursor
9. Cursor for update
10. Exemple complet
1. Programme Bonjour
Un programme PLSQL contient au moins un bloc begin-end avec une instruction.
begin
DBMS_OUTPUT.PUT_LINE('Bonjour');
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 1/12
1/7/24, 7:32 PM PL-SQL-Exemples
end;
Exécution:
SQL>
Faire au préalable set serveroutput on pour activer l'affichage. (les set sont des directives SQLPlus, et ne
marchent pas dans PLSQL.)
Comme pour SQL, les majuscules/minuscules sont indifférents pour les mots clés et les identificateurs.
declare
vEname employee.ename%TYPE;
vSalary employee.Salary%TYPE;
begin
select ename , salary into vEname , vSalary
from employee
where enum = ’E2’;
Il est de bon usage de choisir un moyen de distinguer les variables PLSQL des autres noms utilisés dans une
BD (attributs de relations en général). Le choix fait ici est de commencer le nom de ces variables par la lettre
v suivie du même nom que leur homologue attribut.
Exécution:
10 ...
11 end;
12 /
Nom = Ahmed
Salaire = 6000
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 2/12
1/7/24, 7:32 PM PL-SQL-Exemples
SQL>
A noter:
Les variables sont déclarées du type de l'attribut correspondant. Usage de %TYPE pour récupérer le type
de l'attribut concerné
NB. la déclaration vEname varchar2(20); donnerait le même résultat...
Le select est suivi de into pour affecter le résultat de chaque valeur d'attribut trouvée à la variable
correspondante
La condition where se fait sur la clé E7 , pour s'assurer que le le résultat est un seul tuple. Autrement,
c'est une erreur PLSQL.
L'intérêt de ce programme est quand le critère de recherche n'est pas connu à l'avance ( E2 ici), mais
doit être rentré à l'exécution.
SQL> declare
2 vEname employee.ename%TYPE;
3 vSalary employee.Salary%TYPE;
4 begin
5 select ename , salary into vEname, vSalary
6 from employee
7 where enum = '&num';
8 DBMS_OUTPUT.PUT_LINE('NOM = ' || vEname);
9 DBMS_OUTPUT.PUT_LINE('Salaire = ' || vSalary);
10 end;
11 /
Enter value for num: E7
old 7: where enum = '&num';
new 7: where enum = 'E7';
NOM = Amine
Salaire = 7500
A noter:
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 3/12
1/7/24, 7:32 PM PL-SQL-Exemples
En fait, PLSQL fait un simple remplacement de texte : &num par E7 , la valeur de la variable num à
moment.
Noter aussi que si on avait écrit where enum = &num , sans les apostrophes, cela aurait donné
where enum = E7 . Ce qui est une erreur (laquelle ? Réponse : E7 étant pris comme un identificateur
d'attribut).
Les variables comme num peuvent être saisie au préalable sous SQLPlus avec la commande:
&num est utilisée ici pour initialiser une variable PLSQL vEnum à la déclaration. Celle-ci est ensuite utilisée
dans la clause where . Sans apostrophes (pourquoi?).
Exercice : Afficher la moyenne (avg) des salaires d'un département donné sous le format :
(Format JSON)
declare
vEname employee.ename%TYPE;
vAddress employee.Address%TYPE;
begin
select ename , address into vEname, vAddress
from employee
where salary = &sal;
DBMS_OUTPUT.PUT_LINE('NOM = ' || vEname);
DBMS_OUTPUT.PUT_LINE('Adresse= ' || vAddress);
end;
OK
PLSQL signale une erreur, "retour de plusieurs lignes pour into ", et s'arrête.
On peut gérer cette erreur dans la zone exception, en la récupérant avec l'instruction when ... then .
Le programme suivant prévoit un retour select de plusieurs tuples et traite cette question.
SQL>
declare
vEname employee.ename%TYPE;
vAddress employee.Address%TYPE;
begin
select ename , address into vEname, vAddress
from employee
where salary = &sal;
DBMS_OUTPUT.PUT_LINE('NOM = ' || vEname);
DBMS_OUTPUT.PUT_LINE('Adresse= ' || vAddress);
exception
when TOO_MANY_ROWS then
begin
dbms_output.put_line('SQLCODE: '||SQLCODE);
dbms_output.put_line('Message: '||SQLERRM);
end;
end;
Ici, quand la requête select retourne plusieurs lignes, l'événement est capté et les instructions
correspondantes dans la zone exception sont exécutées. L'événement en l'occurrence est TOO_MANY_ROWS .
Exécution:
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 5/12
1/7/24, 7:32 PM PL-SQL-Exemples
SQL> /
Enter value for sal: 8000
SQLCODE: -1422
Message: ORA-01422: exact fetch returns more than requested number of rows
Le résultat montre le contenu des variables PLSQL SQLCODE et SQLERRM , respectivement le code de l'erreur
(-1422) ayant survenue, et un message explicatif ( ORA -1422... ). On retrouve donc le même message que
l'exemple précédent.
Orale prévoit un certain nombre d'exceptions prédéfinies (named system exception). Voir
http://www.techonthenet.com/oracle/exceptions/named_system.php
On peut utiliser when other then pour capter une certaine erreur et ensuite tester SQLCODE pour déterminer
laquelle.
declare
vEname employee.ename%TYPE;
vAddress employee.address%TYPE;
begin
select ename , salary into vEname, vAddress
from employee
where salary = &sal;
DBMS_OUTPUT.PUT_LINE('NOM = ' || vEname);
DBMS_OUTPUT.PUT_LINE('Adresse = ' || vAddress );
exception
when others then
begin
if SQLCODE = -1422
then dbms_output.put_line('requete multi tuples');
else dbms_output.put_line('Erreur bizarre! Probablement résultat vide.')
end if;
end;
end;
Ici, on teste si l'erreur est de code -1422 , auquel cas il y a eu plusieurs tuples dans select , sinon on sort un
autre message.
Exécution :
SQL > /
Enter value for sal: 8000
requete multi tuples
SQL> /
Enter value for sal: 56 <-- 56 n'existe pas
Erreur bizarre! Probablement résultat vide.
Exercice : Afficher SQLCODE et SQLERRM dans un autre d'exception de votre choix, (e.g. attribut inexistant,
erreur dans expression select ...)
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 6/12
1/7/24, 7:32 PM PL-SQL-Exemples
declare
employeeRec employee%ROWTYPE;
begin
select * into employeeRec
from employee where enum='E7';
DBMS_OUTPUT.PUT_LINE(employeeRec.ename||' '||employeeRec.Salary);
end;
Ici, la notation %ROWTYPE est un type qui permet de déclarer une variable comme un schéma de relation
( employee ici). Il n'y a pas besoin de déclarer des variables supplémentaires de type %TYPE , on a accès aux
champs de la base de données par la notation employeeRec.name etc.
Même exemple avec des champs spécifiés dans select ... into .
declare
employeeRec employee%ROWTYPE;
begin
select ename, salary into employeeRec.ename, employeeRec.Salary
from employee
where enum = 'E8';
DBMS_OUTPUT.PUT_LINE(employeeRec.ename||' '||employeeRec.Salary);
end;
Pour les requêtes select rendant plusieurs lignes, il faut utiliser la notion de cursor . Sorte de fichier de
lignes en mémoire parcourues une à une.
declare
cursor empCursor is
select ename, salary from employee;
employeeRec employee%ROWTYPE;
begin
open empCursor; /* ouverture du Cursor */
loop
/* Accès à chacun des tuples */
fetch empCursor into employeeRec.ename, employeeRec.salary;
exit when empCursor%NOTFOUND;
/* traitement du tuple */
DBMS_OUTPUT.PUT_LINE(employeeRec.ename||' '||employeeRec.Salary);
/* fin traitement tuple */
end loop;
close empCursor;
end;
A noter
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 7/12
1/7/24, 7:32 PM PL-SQL-Exemples
Exercice : Même programme, en utilisant if ...then...else...end if; (au lieu de exit when ) pour tester la
fin du curso r . Quitter la boucle avec l'instruction exit;
On va faire ce calcul en détail dans une boucle sans avoir recours la fonction avg() de SQL.
declare
cursor empCursor is
select * from EMPLOYEE;
employeeRec employee%ROWTYPE;
maxSal employee.SALary%TYPE := 0;
begin
open empCursor;
loop
/* Accès à chacun des tuples */
fetch empCursor into employeeRec;
exit when empCursor%NOTFOUND;
/* traitement du tuple */
if maxSal < employeeRec.salary then
maxSal := employeeRec.salary;
end if;
/* fin traitement tuple */
end loop;
DBMS_OUTPUT.PUT_LINE('Salaire Maximum: '|| maxsal);
close empCursor;
end;
Exercices : Calculer la moyenne des salaires de tous les employés. Calculer la moyenne des salaires du
département D1 .
declare
cursor empCursor is
select * from EMPLOYEE;
employeeRec employee%ROWTYPE;
maxSal employee.SALary%TYPE := 0;
begin
open empCursor;
fetch empCursor into employeeRec;
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 8/12
1/7/24, 7:32 PM PL-SQL-Exemples
close empCursor;
end;
Noter ici
declare
employeeRec employee%ROWTYPE;
cursor empCursor (d varchar2) is /* paramètre formel d*/
select ename , salary from employee where dept = d;
begin
open empCursor ('D1'); /* paramètre effectif D1*/
loop
/* Accès à chacun des tuples */
fetch empCursor into employeeRec.ename, employeeRec.salary;
exit when empCursor%NOTFOUND;
/* traitement du tuple */
DBMS_OUTPUT.PUT_LINE(employeeRec.ename||' '||employeeRec.Salary);
/* fin traitement tuple */
end loop;
close empCursor;
end;
/
Dans un itérateur abstrait on n'a pas besoin de spécifier comment faire le parcours d'une itération, c'est à dire
l'initialisation, comment passer au tour suivant et la condition d'arrêt. On ne spécifie que le traitement à faire
sur chaque éléments à traiter.
declare
cursor empCursor is
select * from EMPLOYEE;
maxSal employee.SALary%TYPE := 0;
begin
for employeeRec in empCursor loop
/* traitement du tuple */
if maxSal < employeeRec.salary then
maxSal := employeeRec.salary;
end if;
/* fin traitement tuple */
end loop;
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 9/12
1/7/24, 7:32 PM PL-SQL-Exemples
DBMS_OUTPUT.PUT_LINE('Salaire Maximum: '|| maxsal);
end;
Noter
Exercice : Faire un programme PL/SQL qui imprime la table employee en format JSON.
{"enum":"E7", "ename": "Amine", "salary":"7500", "address":"Fes", "dept": "D2"}
{"enum":"E6", "ename": "Aziz", "salary":"8500", "address":"Casa", "dept": "D1"}
...
declare
cursor empCursor is
select * from employee for update of salary;
begin
for employeeRec in empCursor
loop
update employee set salary = salary * 1.2
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 10/12
1/7/24, 7:32 PM PL-SQL-Exemples
where current of empCursor ;
end loop;
end;
On augmente de 25% le salaire des employés d'un département donné, et si le nouveau salaire est supérieure
à 10000, on rajoute l'employé concerné dans une table veterans (supposée créée).
NO_DATA_FOUND , exception prédéfinie, quand le résultat est vide ou qu'on en a atteint la fin.
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 11/12
1/7/24, 7:32 PM PL-SQL-Exemples
tropGrand ,
exception utilisateur pour la cas où un salaire est >10000. Auquel cas,
toute autre exception sera traitée en affichant SQLCODE et SQLERRM .
Exécution :
SQL> /
Enter value for dept: D34
Retrouvé :
pas trouve ou fin de cursor
SQL> /
NUM SAL
--- ----------
E6 10625
Exercice :
1. Il ne distingue pas entre les cas " cursor vide au début" et "fin de parcours du cursor "
2. Il se termine dès qu'il trouve un gros salaire, et ne traite pas les suivants.
https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 12/12