Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Cours

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 12

1/7/24, 7:32 PM PL-SQL-Exemples

PLSQL Par L'Exemple


Najib Tounsi
Lien permanent https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html

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

NB. Les exemples qui suivent portent sur la base de données :

SQL> select * from employee;

ENUM ENAME SALARY ADDRESS DEPT


----- ------------ ---------- ---------- -----
E7 Amine 7500 Fes D2
E6 Aziz 8500 Casa D1
E5 Amina 8000 Rabat D3
E4 Said 5000 Agadir D3
E3 Fatima 7000 Tanger D2
E2 Ahmed 6000 Casa D1
E1 Ali 8000 Rabat D1
E8 Ahmed 4000 Casa D4
 

1. Programme Bonjour
Un programme PLSQL contient au moins un bloc begin-end avec une instruction.

Exemple 1 : programme bonjour

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> set serveroutput on


SQL> begin
2 DBMS_OUTPUT.PUT_LINE('Bonjour');
3 end;
4 /
Bonjour

PL/SQL procedure successfully completed.

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.

L'exécution se fait par ' / ' en début de ligne.

2. Variables Et Requête Mono-Tuple


PLSQL est fait pour programmer sur la base de données. Le programme suivant affiche le nom et le salaire
d'un employé donné.

Exemple 2 : Requête SELECT ... INTO

declare
vEname employee.ename%TYPE;
vSalary employee.Salary%TYPE;
begin
select ename , salary into vEname , vSalary
from employee
where enum = ’E2’;

DBMS_OUTPUT.PUT_LINE('Nom = ' || vEname ); /* || opérateur de concaténation


DBMS_OUTPUT.PUT_LINE('Salaire = ' || vSalary);
end;
/
 

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

PL/SQL procedure successfully completed.

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.

Exercice : Afficher aussi l'adresse de l'employé donné.

3. Variables Et Requête Mono-Tuple. Critère


De Recherche Saisi À L'exécution
Il sera fait appel ici, à une "variable" num, dont la valeur sera saisie à l'exécution. C'est en fait une variable
SQLPlus qui sera utilisée dans PLSQL avec la notation &num . Le programme est donné en même temps que
son 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

PL/SQL procedure successfully completed.


 

A noter:

L'apparition de la variable à la ligne 7, dans la clause where .


A l'exécution de select , PLSQL demandera la saisie de la variable. message " Enter value for num: ".
On tape E7.
Constater ce qu'a fait ensuite PLSQL : avec old, il affiche la ligne source du programmeur, et avec
new, il signale la ligne qu'il va exécuter.

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:

SQL> ACCEPT num PROMPT 'Entrer n° employé: '


 

4. Même Exemple Avec &Num Utilisée Dans La


Zone Declare
declare
vEname employee.ename%TYPE;
vSalary employee.Salary%TYPE;
vEnum varchar2(10) := '&num';
begin
select ename , salary into vEname, vSalary
from employee
where enum = vEnum;
DBMS_OUTPUT.PUT_LINE('NOM = ' || vEname);
DBMS_OUTPUT.PUT_LINE('Salaire = ' || vSalary);
end;
 

&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 :

{"nom département" : valeur, "moyenne des salaires" : valeur}

(Format JSON)

5. Recherche Avec Salaire, Non Forcément


Unique
Même type de programme

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;
 

Deux exécutions, salaire unique (7500) et non unique (8000) :


https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 4/12
1/7/24, 7:32 PM PL-SQL-Exemples

Enter value for sal: 7500


old 7: where salary = &sal;
new 7: where salary = 7500;
NOM = Amine
Adresse= Fes

PL/SQL procedure successfully completed.


 

OK

Enter value for sal: 8000


old 7: where salary = &sal;
new 7: where salary = 8000;
declare
*
ERROR at line 1:
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 5
 

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 .

6. Rajout De Zone D'exception


On rajoute une partie après la zone declare et begin . Cette zone sert à prévoir des erreurs (exceptions)
éventuelles, et contiendra une règle de type:

when événement then begin ... end;

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

PL/SQL procedure successfully completed.


 

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

PL/SQL procedure successfully completed.

SQL> /
Enter value for sal: 56 <-- 56 n'existe pas
Erreur bizarre! Probablement résultat vide.

PL/SQL procedure successfully completed.


 

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

7. Variable De Type Schéma De Table


Même exemple où on récupère toute un tuple d'une relation.

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;
 

8. Requête Select Nécessitant Un Cursor

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.

8.1. Exemple Simple: On Affiche Les Noms Et Salaires

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

open ,qui crée le cursor en exécutant le select


fetch pour extraire une à une les lignes du cursor retournées par la requête
exit when %NOTFOUND , qui arrête la boucle quand on a tout parcouru
close , qui termine le travail à faire avec le cursor

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;

8.2. Autre Exemple, Calcul Du Salaire Maximum

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 .

8.3. Idem Avec While

declare
cursor empCursor is
select * from EMPLOYEE;
employeeRec employee%ROWTYPE;
maxSal employee.SALary%TYPE := 0;
begin
open empCursor;
fetch empCursor into employeeRec;

while empCursor%FOUND loop


/* traitement du tuple */
if maxSal < employeeRec.salary then
maxSal := employeeRec.salary;
end if;
/* fin traitement tuple */
fetch empCursor into employeeRec;
end loop;
DBMS_OUTPUT.PUT_LINE('Salaire Maximum: '|| maxsal);

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

pour itérer tant qu'un tuple est trouvé (par fetch )


%FOUND
Une lecture avant la boucle donc, et en fin de chaque itération

8.4. Cursor Paramétré

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;
/
 

A la déclaration de cursor , on indique un paramètre avec son type : d varchar2 ici.

A l'ouverture de cursor , on donne la valeur au paramètre. Une constante ici 'D1' .

8.5. Itérateur Abstrait FOR

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

l'absence de open / close (automatiques)


pas besoin fetch , ni au début ni en cours de boucle
pas besoin de condition d'arrêt
la variable de contrôle employeeRec déclarée automatiquement du type du schéma de résultat de
select .

8.6. Forme Sans Déclarer Cursor

cursor "anonyme". On spécifie le select directement dans le for .


 
begin
for untel in (select enum, ename, salary from employee )
loop
dbms_output.put_line('num = ' || untel.enum ||
', nom = ' || untel.ename ||
', salaire = '|| untel.salary);
end loop;
end;
 

Variable de contrôle untel déclarée automatiquement du type du résultat de select .

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"}
...

On pourra aussi le faire avec un format CSV. Par exemple :


enum;ename;salary;address;dept
E7;Amine;7500;Fes;D2
E6;Aziz;8500;Casa;D1
...

Les noms de colonne sont en première ligne.

9. Cursor For Update


On peut utiliser un cursor pour mettre à jours des champs des lignes retrouvées. Ces mises à jours seront
appliquées aux tables de base. Mais auparavant il faut avertir le SGBD pour cela. A la déclaration du cursor
on rajoute une clause for update .

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;
 

10. Exemple Complet


(voir le polycopié
 PLSQL p27) 

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).

1 delete from veterans ;


2 declare
sal employee.salary%TYPE;
3 num employee.enum%TYPE;
4 TAUX number (5,2) := 1.25;
5 tropGrand exception;
6 CURSOR empCurseur is select enum , salary
7 from employee
where dept = '&dept' order by salary desc
8 for update of SaLary;
9 begin
10 OPEN empCurseur;
11 loop
12 FETCH empCurseur INTO num, sal;
DBMS_OUTPUT.PUT_LINE('Retrouvé : '||num || ' ' || sal);
13 if empCurseur%NOTFOUND then RAISE NO_DATA_FOUND; end if;
14
15 update employee set SaLary = sal * TAUX
16 where current of empCurseur ;
DBMS_OUTPUT.PUT_LINE (num || ' mis a jour.');
17 DBMS_OUTPUT.PUT_LINE (' Nouveau salaire : ' || sal*TAUX );
18
19 if sal * TAUX >= 10000 then RAISE tropGrand; end if ;
20 end loop;
21 exception
when NO_DATA_FOUND then
22 DBMS_OUTPUT.PUT_LINE('pas trouve ou fin de cursor');
23 rollback;
24
25 when tropgrand then
26 insert into veterans values(num, sal*TAUX);
DBMS_OUTPUT.PUT_LINE(num||' '||Sal *TAUX|| ' Trop grand');
27 commit;
28
29 when others then
30 begin
dbms_OUTPUT.PUT_LINE('SQLCODE: '||SQLCODE);
31 dbms_OUTPUT.PUT_LINE('Message: '||SQLERRM);
32 end;
33
34 end;
35 /
 

Créer au préalable la table veterans.

Noter les exceptions soulevées par raise

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

PL/SQL procedure successfully completed.

SQL> /

Enter value for dept: D1


Retrouvé : E6 8500
E6 mis a jour.
Nouveau salaire : 10625
E6 10625 Trop grand

PL/SQL procedure successfully completed.

SQL> select * from veterans;

NUM SAL
--- ----------
E6 10625
 

Exercice :

Ce programme est à améliorer.

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.

Améliorez-le pour prendre en compte ces cas.

https://www.emi.ma/ntounsi/COURS/DB/Polys/SQL/Exercices/PL-SQL-Exemples.html 12/12

Vous aimerez peut-être aussi