Vous êtes sur la page 1sur 22

Ministère de l’Enseignement Supérieur et de recherche scientifique

Université de Sousse

Séance de Cours BD Avancé

Chapitre5
PL/SQL : Les curseurs

2021-2022
I. Introduction
 Les ordres SQL sont exécutés et manipulés dans des espaces de travail.
 Un curseur, permet d’attribuer des noms à ces espaces de travail et d’accéder à leurs
données.

Il existe deux types de curseurs :

 les curseurs implicites : crées de manière implicite par PL/SQL pour


chaque ordre SQL, même pour ceux qui ne renvoient qu’une ligne.

 les curseurs explicites : crées par le programmeur pour pouvoir manipuler


les données des requêtes qui renvoient plus d’une ligne.

2
II. Les Curseurs implicites
 PL/SQL utilise un curseur implicite pour chaque opération du LMD de SQL
(INSERT, UPDATE et DELETE).
 Ce curseur porte le nom SQL et il est exploitable après avoir exécuté l’instruction.

 Les attributs de curseurs implicites permettent de connaître un certain nombre


d’informations qui ont été renvoyées après l’instruction du LMD et qui peuvent être
utiles au programmeur.

 Ces attributs peuvent être employés dans une section de traitement ou d’exception.
Les principaux attributs sont les suivants :

3
II. Les Curseurs implicites
Exemple 1 : curseur implicite

DECLARE
nb_ligne number:=0; --nombre de lignes mise à jour.
BEGIN
UPDATE emp SET sal = sal*1.1
WHERE deptno = 10;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Aucun employé n a été augmenté');
ELSE
nb_ligne :=SQL%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE(nb_ligne||'employés ont été augmenté');
END IF;
END;
4
III. Les curseurs explicites
III.1 Présentation d’un curseur
 Permettent de manipuler les données des requêtes qui renvoient plusieurs lignes.
Tout curseur utilisé dans un bloc PL/SQL doit obligatoirement être déclaré dans la
section DECLARE du bloc.
Syntaxe :
CURSOR nom_curseur IS ordre_select ;
Exemple
DECLARE
-- déclaration de variables ...;
-- déclaration de curseurs
CURSOR c_employe IS SELECT ename, deptno, sal FROM emp
WHERE job = ‘MANAGER’
ORDER BY ename ;
BEGIN
...;
END;
5
III. Les curseurs explicites
III.2 Les étapes de la vie d’un curseur
Les étapes de manipulation d'un curseur explicite sont décrites dans le schéma suivant :

OPEN : exécute l’ordre SQL associé au curseur, identifie le résultat et positionne


le curseur avant le premier enregistrement.
 FETCH retourne l’enregistrement courant puis place le curseur sur
l’enregistrement suivant.
 CLOSE permet de fermer le curseur et libère les ressources allouées.

Remarque :
Ne pas utiliser la clause INTO dans l'instruction SELECT.
On peut déclarer autant de curseurs que l'on veut.
Le contenu du curseur n'est pas ramené au moment de sa déclaration, mais au
moment de son ouverture. 6
III. Les curseurs explicites
III.3 Ouverture d’un curseur explicite et traitement de ses lignes
 Cette phase d’ouverture s’effectue dans la section BEGIN du bloc
Syntaxe : OPEN nom_curseur ;
 Dès que vous ouvrez le curseur, l’exécution de l’ordre SQL est lancée.
III.4 Traitement des lignes d’un curseur explicite
Il s’agit de
1. récupérer les lignes de l’ordre SELECT en utilisant la commande FETCH
2. les traiter une par une, en stockant la valeur de chaque colonne de l’ordre SQL dans
une variable réceptrice.
Syntaxe : FETCH nom_curseur INTO liste_de_variables;

 La commande FETCH ne retourne qu’un enregistrement.


 Pour récupérer l’ensemble des enregistrements de l’ordre SQL, il faut prévoir
une boucle.

7
III. Les curseurs explicites : exemple2
DECLARE
v_nom emp.ename%TYPE; Create table tabRes( nom varchar,
v_fonction emp.job%TYPE; Fonction varchar,
Salaire varchar);
v_salaire emp.sal%TYPE;
-- déclaration de curseurs
CURSOR c_employe IS SELECT ename, job, sal FROM emp WHERE upper(job) =
‘MANAGER’ ORDER BY ename ;
BEGIN
OPEN c_employe ;
LOOP
FETCH c_employe INTO v_nom, v_fonction, v_salaire ;
IF (upper(v_fonction )= ‘CLERC’ AND v_salaire < 1000) THEN
INSERT INTO tabRes VALUES ( v_nom, v_fonction, v_salaire );
END IF;
EXIT WHEN upper(v_nom ) = ‘SCOTT’;
END LOOP;
CLOSE c_employe ;-- fermeture du curseur
END;
8
III. Les curseurs explicites
III.4 Les attributs d’un curseur explicite
nom_du_curseur%FOUND = TRUE, si le dernier FETCH a retourné une ligne.
nom_du_curseur%NOTFOUND = TRUE, si le dernier FETCH n’a pas renvoyé de ligne.
nom_du_curseur %ISOPEN : permet de vérifier si le curseur considéré a été ouvert.
nom_du_curseur %ROWCOUNT : traduit l’énième ligne retournée par le FETCH.

Notes :

FETCH est utilisé seulement avec les curseurs explicites


Inclure le même nombre de variables dans la clause INTO qu'il y a d'attributs dans
le SELECT associé au curseur
Mettre les variables dans le même ordre
Utiliser le test %FOUND ou %NOTFOUND pour voir si la fonction FETCH a
permis d'atteindre un nouveau tuple, ou si l'on est à la fin du curseur
Avant le premier appel à la fonction FETCH, l'attribut %NOTFOUND du curseur
vaut NULL  pensez y pour éviter des boucles infinies 9
III. Les curseurs explicites
Exemple 3 d’utilisation d’un curseur explicite

Dans l’exemple suivant, nous recherchons le salaire de chaque employé ainsi que
la moyenne des salaires des employés qui font le même métier que lui.
La table résultat suivante va contenir le résultat du curseur.

CREATE TABLE resultat(


nom varchar2(30),
salaire number(7,2),
avgsal number(7,2));

10
DECLARE
v_nom emp.ename%TYPE;
v_salaire emp.sal%TYPE;
v_fonction emp.job%TYPE;
v_moyenne NUMBER(7,2);
CURSOR c_employe IS SELECT ename, sal, job FROM emp ORDER BY ename ;
BEGIN
IF NOT (c_employe%ISOPEN) THEN -- test de l'ouverture du curseur
OPEN c_employe ;
END IF;
LOOP
FETCH c_employe INTO v_nom, v_salaire , v_fonction ;
-- tester %NOTFOUND juste après le FETCH !
EXIT WHEN c_employe%NOTFOUND ; --sortie boucle
-- l'ordre SQL suivant est implicite (pas de curseur car une seule valeur retournée
SELECT avg(sal) INTO v_moyenne FROM emp WHERE job = v_fonction ;
-- stockage des résultats dans une table
INSERT INTO resultat VALUES ( v_nom, v_salaire, v_moyenne );
END LOOP;
END; /

11
Exemple 4 d’utilisation d’un curseur explicite

Dans cet autre exemple, nous interrompons le traitement d’un curseur si plus de 1 000
enregistrements sont traités.
DECLARE
v_nom emp.ename%TYPE;
v_salaire emp.sal%TYPE;
v_fonction emp.job%TYPE;
v_moyenne NUMBER(7,2);
-- déclaration de curseurs
CURSOR c_employe IS SELECT ename, sal, job FROM emp ORDER BY ename ;
BEGIN
OPEN c_employe ; -- ouverture du curseur
LOOP
FETCH c_employe INTO v_nom,v_salaire , v_fonction ;
-- tester %NOTFOUND juste après le FETCH !
EXIT WHEN ( c_employe%NOTFOUND OR c_employe%ROWCOUNT > 1000);
-- traitement à effectuer ...
END LOOP;
CLOSE c_employe ;
END;
12
IV. Simplification d’écriture des curseurs
IV.1 Simplification de la déclaration
Il est possible de déclarer implicitement une structure dont les éléments sont du même
type que les colonnes retournées par un curseur. la syntaxe est :

DECLARE
CURSOR mon_curseur IS mon_ordre_select ;
nom_structure mon_curseur%ROWTYPE ;
--Pour utiliser les colonnes de la structure :
nom_structure.nom_colonne_definie_dans_mon_ordre_select
--La structure est renseignée par le FETCH :
FETCH mon_curseur INTO nom_structure ;

13
IV. Simplification d’écriture des curseurs
IV.2 Simplification d’écriture des curseurs : FOR… LOOP
La syntaxe FOR… LOOP permet de simplifier les étapes OPEN, FETCH, CLOSE
de la vie d’un curseur. Elle les déclenche automatiquement.
DECLARE
CURSOR mon_curseur IS
SELECT ename, job, sal FORM emp ;
-- il n’est plus nécessaire de déclarer votre structure !
...
BEGIN
FOR rec_employe IN mon_curseur LOOP
-- ouverture automatique du curseur !
-- FETCH automatique du curseur !
-- condition de sortie %NOTFOUND automatique !
-- votre traitement
...
END LOOP;
-- fermeture automatique du curseur !
END;

14
IV. Simplification d’écriture des curseurs
IV.2 Simplification d’écriture des curseurs : FOR… LOOP
Exemple 5 : dans l'exemple 3 le curseur explicite est parcouru en utilisant une boucle LOOP.
Il peut être modifié en utilisant une boucle FOR … LOOP comme suit :
DECLARE
v_moyenne NUMBER(7,2);
-- déclaration de curseurs
CURSOR c_employe IS SELECT ename, sal, job FROM emp
ORDER BY ename ;
BEGIN
FOR ma_structure IN c_employe LOOP
SELECT avg(sal) INTO v_moyenne FROM emp WHERE job = ma_structure.job;
-- stockage des résultats dans une table
insert into resultat values ( ma_structure.ename, ma_structure.sal,v_moyenne );
END LOOP;
END;

15
IV. Simplification d’écriture des curseurs
IV.3 Simplification d’écriture des curseurs : FOR… IN
La syntaxe FOR… IN est encore plus concise. Elle évite de déclarer le curseur dans la
section DECLARE.
DECLARE
-- pas de déclaration de curseur
-- pas de déclaration de structure
BEGIN
FOR ma_structure IN ( SELECT col1, col2 , ..., coln FROM table );
LOOP
-- le FETCH automatique !
-- la condition de sortie est automatique !
...
-- suite du traitement ...
-- on utilise les variables par :
... ma_structure.col1 ...
... ma_structure.col2 ...
... ma_structure.coln ...
END LOOP;
-- fermeture automatique !
END;
16
IV. Simplification d’écriture des curseurs
IV.3 Simplification d’écriture des curseurs : FOR… IN
Exemple 6 : dans l'exemple 3 le curseur explicite est parcouru en utilisant une boucle LOOP.
Il peut être modifié en utilisant une boucle FOR … IN comme suit :

DECLARE
v_moyenne NUMBER(7,2);
-- pas de déclaration de curseurs
BEGIN
FOR ma_structure IN (SELECT ename, sal, job FROM emp ORDER BY ename )
LOOP
SELECT avg(sal) INTO v_moyenne FROM emp WHERE job = ma_structure.job;
-- stockage des résultats dans une table
insert into resultat values( ma_structure.ename, ma_structure.sal,v_moyenne );
END LOOP;
END;
17
V. Les curseurs paramétrés
Dans tous les curseurs précédents, les ordres SQL étaient fixes et n’acceptaient aucun
paramètre.
Il est souvent utile de réutiliser un même curseur avec des valeurs différentes, dans un
même bloc PL/SQL
DECLARE
CURSOR mon_curseur (param1 TYPE, param2 TYPE, ... )
IS mon_ordre_select ; -- l’ordre select utilise les paramètres param1, param2...
BEGIN
OPEN mon_curseur (val1, val2,...) ;
...
CLOSE mon_curseur;
END;

 Les types des paramètres sont les suivants : CHAR, NUMBER, DATE, BOOLEAN, .

 Leurs longueurs n’est pas spécifiée.

 Le passage des valeurs des paramètres s’effectue à l’ouverture du curseur.


18
V. Les curseurs paramétrés
Exemple 7
DECLARE
v_nom emp.ename%TYPE;
v_salaire emp.sal%TYPE;
v_fonction emp.job%TYPE;
v_moyenne NUMBER(7,2);
CURSOR c_employe(v_deptno number)IS
SELECT ename, sal, job FROM emp where deptno = v_deptno
ORDER BY ename ;
BEGIN
FOR ma_structure IN c_employe(10) LOOP
SELECT avg(sal) INTO v_moyenne FROM emp
WHERE job = ma_structure.job;
-- stockage des résultats dans une table
INSERT INTO resultat
VALUES ( ma_structure.ename, ma_structure.sal,v_moyenne );
END LOOP;
END;

19
V.I Mettre à jour des données à travers un curseur
 Pour verrouiller les lignes d’une table interrogée par un curseur dans le but de mettre à
jour la table, sans qu’un autre utilisateur ne la modifie en même temps, il faut utiliser
la clause FOR UPDATE.
 Elle s’utilise lors de la déclaration du curseur et verrouille les lignes concernées
lorsque le curseur est ouvert. Les verrous sont libérés à la fin de la transaction.

Syntaxe : CURSOR nomCurseur[(paramètres)] IS


SELECT … FROM {nomTable | nomVue } WHERE …
FOR UPDATE [OF [[schéma.] {nomTable | nomVue }.]colonne [, …]
[ NOWAIT | WAIT entier ]

 La directive OF permet de connaître les colonnes à verrouiller. Sans elle, toutes les
colonnes issues de la requête seront verrouillées.
 NOWAIT précise de ne pas faire attendre le programme si les lignes demandées sont
verrouillées par une autre session.
 WAIT spécifie le nombre de secondes à attendre au maximum avant que les lignes
soient déverrouillées par une autre session.
Sans NOWAIT et WAIT, le programme attend que les lignes soient disponibles.20
V.I Mettre à jour des données à travers un curseur : CURRENT-OF
Exemple 8 : curseur mettant à jour les données retournées
DECLARE
CURSOR mon_curseur IS SELECT ename, sal, comm
FROM emp
FOR UPDATE OF sal ;
BEGIN
FOR ma_structure IN mon_curseur LOOP
IF ma_structure.comm IS NOT NULL THEN
-- si pas de commission, le salaire augmente de 5%
UPDATE emp SET sal = sal*1.05
WHERE CURRENT OF mon_curseur;
END IF;
END LOOP;
-- ne pas oublier de valider les modifications
COMMIT;
END;
21
V.I Mettre à jour des données à travers un curseur : CURRENT-OF
Remarque
Une validation (COMMIT) ou une annulation (ROLLBACK) avant la fermeture d'un
curseur utilisant FOR UPDATE fait perdre la position du curseur et déclenche une
erreur.
 L'instruction WHERE CURENT OF est uniquement utilisée avec les curseurs
déclarés FOR UPDATE

 Il n'est pas possible de déclarer un curseur FOR UPDATE en utilisant dans la requête :
Les directives DISTINCT ou GROUP BY
Un opérateur ensembliste ou
Une fonction d'agrégat
Une transaction est initialisée par la première commande LMD, et doit être suivi d’un
COMMIT ou d’un ROLLBACK

Utilisez les instructions SQL COMMIT ou ROLLBACK pour terminer de façon


explicite une transaction.

22

Vous aimerez peut-être aussi