Présente une description complète sur les curseurs avec exemples et exercices corrigés. Ainsi, vous allez trouver:
- Définition des curseurs
- Curseurs implicites
- Curseurs explicites
- Les curseurs paramétrables
- Accès concurrents (FOR UPDATE)
- Variables de types Curseurs: Curseurs non typés et typés
-
2. CURSEURS
INTRODUCTION
Chaque instruction SQL exécutée par le SGBD possède
un espace SQL privé qui contient des informations sur
l'instruction SQL et l'ensemble de données retournées:
Est-ce que la requête SQL a affecté un enregistrement ou
non,
Nombre de lignes qui sont affectées par une requête.
Comment connaitre ces informations en
PL/SQL après l’exécution d’une requête
SQL???
2
3. CURSEURS
INTRODUCTION
En définit en PL/SQL un curseur comme un espace
mémoire qui contient le résultat d’une requête
SQL.
PL/SQL utilise des curseurs pour tous les accès à
des informations de la base de données.
On peut dire que toutes les requêtes sont associées à
un curseur.
On distingue deux types de curseurs SQL:
Le curseur implicite: utilisé par le serveur Oracle
pour tester et analyser les requêtes SQL,
les curseurs explicites : déclarés par les
programmeurs. 3
4. CURSEUR IMPLICITE
Il est associé aux ordres SELECT, INSERT, DELETE
et UPDATE.
Il est déclaré automatiquement pour chaque opération
du LMD (Langage de Manipulation de Données) de
SQL.
NB: une requête SELECT ne doit retourner qu'un seul
enregistrement.
Ce curseur, par défaut, porte le nom SQL (contrario
aux curseurs explicites où l’utilisateur spécifie un nom
pour le curseur) et il est exploitable après l’exécution
d’une instruction.
L’exécution d’une nouvelle instruction remplace l’ancien
curseur par un nouveau 4
5. CURSEUR IMPLICITE
LES ATTRIBUTS D’UN CURSEUR
Chaque curseur a des attributs.
Ces attributs permettent de connaître un certain
nombre d’informations qui ont été renvoyées
après l’instruction du LMD SQL et qui peuvent
être utiles au programmeur.
Ils sont utilisés dans la section d’exécution ou
dans la section d’exceptions.
5
6. CURSEUR IMPLICITE
LES ATTRIBUTS D’UN CURSEUR
Les principaux attributs sont les suivants:
6
Variable Attribut Description
%FOUND SQL%FOUND Booléen valant TRUE si la
dernière instruction LMD
affecte au moins un
enregistrement.
%NOTFOUND SQL%NOTFOUND Booléen valant TRUE si la
dernière instruction LMD
n’affecte aucun enregistrement.
%ROWCOUNT SQL%ROWCOUNT Nombre de lignes affectées par
la dernière instruction LMD.
7. CURSEUR IMPLICITE
Après exécution, le code PL/SQL affiche le nombre
d’enregistrements mises-à-jour
7
DECLARE
v_salaire NUMBER := 3000;
v_count NUMBER;
BEGIN
UPDATE E_EMP SET SALAIRE = v_salaire WHERE NOM= ‘HASSAN’;
v_count :=SQL%ROWCOUNT;
dbms_output.put_line(v_count );
END;
/
Exemple 1
8. CURSEUR IMPLICITE
Exemple 2
8
VARIABLE g_count NUMBER
DECLARE
v_salaire NUMBER := 3050;
v_count NUMBER;
BEGIN
UPDATE E_EMP SET SALAIRE = v_salaire +500 WHERE NOM=
'HANIN' or NOM= 'HAMDI' ;
UPDATE E_EMP SET SALAIRE = v_salaire WHERE NOM=
'HASSAN';
:g_count := SQL%ROWCOUNT ;
commit;
END;
/
PRINT g_count
9. CURSEUR IMPLICITE
Exercice:
Ecrire un programme qui permet de:
Saisir en entrée le nom d'un employé,
Vérifier que cet employé est présent dans la table
E_EMP. Si oui le supprimer.
9
10. CURSEUR IMPLICITE
Solution
10
SET VERIFY OFF
ACCEPT nom_emp PROMPT 'nom employé : '
VARIABLE g_result VARCHAR2(50);
BEGIN
DELETE from E_EMP WHERE nom= '&nom_emp';
if SQL%FOUND THEN
:g_result:='l enregitsrement ' || '&nom_emp' || ' est
supprimé';
ELSE
:g_result:='Aucun enregistrement pour le nom: ' ||
'&nom_emp' || ' n existe';
END IF;
COMMIT;
END;
/
print g_result;
11. CURSEUR IMPLICITE
Les curseurs implicites sont utilisés par le
serveur Oracle pour tester et analyser les
requêtes SQL,
Les attributs: %FOUND, %NOTFOUND,
%ROWCOUNT
Comment faire pour traiter des requêtes
SELECT qui renvoies plusieurs
enregistrements??
11
12. CURSEURS EXPLICITES
Pour traiter une requête SELECT qui retourne
plusieurs lignes, l'utilisateur doit définir un
curseur qui lui permet d'extraire la totalité des
lignes sélectionnées.
On parle des curseurs explicites (ou programmables)
Le traitement du SELECT se fera ligne par ligne.
12
13. CURSEURS EXPLICITES
L'utilisation d'un curseur explicite (pour traiter
un ordre SELECT ramenant plusieurs lignes)
nécessite 4 étapes :
1. Déclaration du curseur,
2. Ouverture du curseur,
3. Traitement des lignes,
4. Fermeture du curseur.
13
14. CURSEURS EXPLICITES
DÉCLARATION
La déclaration du curseur permet de stocker l'ordre (la
requête) SELECT dans le curseur.
Un curseur est déclaré dans la partie DECLARE d’un
bloc PL/SQL.
La syntaxe:
14
CURSOR nomcurseur IS la_requête_SELECT ;
Exemple:
DECLARE
CURSOR c_product IS SELECT name, mark, QtyInStock FROM T_Product
WHERE ID=1;
…
15. CURSEURS EXPLICITES
OUVERTURE ET FERMETURE
Une fois un curseur explicite est déclaré, et avant de
l’utiliser, il faut l’ouvrir.
L’ouverture du curseur est en fait l’exécution de la
requête déclarée par celui-ci.
L'étape d'ouverture permet d'effectuer les opérations
suivantes :
l'allocation mémoire du curseur,
l'analyse syntaxique de la requête,
le positionnement de verrous éventuels (si select for
update...).
Alors que la fermeture permet de libérer la place
mémoire allouée par l’ouverture. 15
16. CURSEURS EXPLICITES
OUVERTURE ET FERMETURE
L’ouverture et la fermeture du curseur est effectuée
dans la partie exécution du bloc PL/SQL.
16
BEGIN
…
OPEN nomcurseur;
…
CLOSE nomcurseur;
…
END;
NB1 : Il faut bien faire attention à n’ouvrir que les curseurs qui sont
déjà déclarés,
NB2 : Il faut faire attention à ne fermer que les curseurs ouverts.
17. CURSEURS EXPLICITES
OUVERTURE ET FERMETURE
Exemple
17
DECLARE
CURSOR c_product IS SELECT name, mark, QtyInStock FROM
T_Product WHERE ID=1;
BEGIN
OPEN c_product;
… /*Traitement des lignes*/
CLOSE c_product ;
END;
18. CURSEURS EXPLICITES
LE TRAITEMENT DES ENREGISTREMENTS
Un des avantages des curseurs est de pouvoir traiter
(un par un) plusieurs enregistrements renvoyés par
une requêtes SQL.
L’extraction de lignes est réalisée avec l'instruction
FETCH. … INTO.
FETCH (Anglais): rapporter (français)
La syntaxe est la suivante :
18
FETCH nomcurseur INTO nomvariable1 [,nomvariableI] … | nomrecord;
19. CURSEURS EXPLICITES
LE TRAITEMENT DES ENREGISTREMENTS
Exemple
19
DECLARE
CURSOR c_emp IS SELECT NO, NOM, SALAIRE FROM e_emp;
v_NO e_emp.no%TYPE;
v_NOM e_emp.NOM%TYPE;
v_SALAIRE e_emp.SALAIRE%TYPE;
BEGIN
OPEN c_emp;
FETCH c_emp INTO v_NO, v_NOM, v_SALAIRE;
dbms_output.put_line('NO: ' || v_NO || chr(9) || 'NOM: ' || v_NOM || chr(9) ||
' SALAIRE:' || v_SALAIRE);
CLOSE c_emp;
END;
L'ordre FETCH ne ramène qu'une seule ligne à la fois.
La ligne courante d’un curseur est déplacée à chaque appel de l’instruction
FETCH
Si le curseur est en fin de tableau, la commande Fetch ne quitte pas
automatiquement le tableau, elle reste « bloqué e» sur le dernier
enregistrement.
20. CURSEURS EXPLICITES
LE TRAITEMENT DES ENREGISTREMENTS
Exemple
20
DECLARE
CURSOR c_emp IS SELECT *FROM e_emp;
rty_employe e_emp%ROWTYPE;
BEGIN
OPEN c_emp;
FETCH c_emp INTO rty_employe;
dbms_output.put_line('NO: ' || rty_employe.NO || chr(9) || 'NOM: '
|| rty_employe.NOM || chr(9) || ' SALAIRE:' || rty_employe.SALAIRE);
FETCH c_emp INTO rty_employe;
dbms_output.put_line('NO: ' || rty_employe.NO || chr(9) || 'NOM: '
|| rty_employe.NOM || chr(9) || ' SALAIRE:' || rty_employe.SALAIRE);
CLOSE c_emp;
END;
21. CURSEURS EXPLICITES
LES ATTRIBUTS D’UN CURSEUR
Les principaux attributs d’un curseur explicites
sont les suivants:
21
Variable Attribut Description
%FOUND Cur%FOUND Booléen valant TRUE si la dernière
instruction LMD affecte au moins un
enregistrement.
%NOTFOUND Cur%NOTFOUND Booléen valant TRUE si la dernière
instruction LMD n’affecte aucun
enregistrement.
%ROWCOUNT Cur%ROWCOUNT Nombre de lignes affectées par la dernière
instruction LMD.
%ISOPEN Cur% ISOPEN Cet attribut est de type booléen : la valeur
de l’attribut est TRUE si le curseur est
ouvert et FALSE si le curseur est fermé
22. CURSEURS EXPLICITES
LES ATTRIBUTS D’UN CURSEUR
Exercice:
Ecrivez un programme PL/SQL qui permet
d’afficher tout les enregistrements (champs No,
Nom et Salaire) de la table E_EMP en utilisant la
structure répétitive WHILE-LOOP
22
23. CURSEURS EXPLICITES
LES ATTRIBUTS D’UN CURSEUR
23
DECLARE
CURSOR c_emp IS SELECT *FROM e_emp;
rty_employe e_emp%ROWTYPE;
BEGIN
OPEN c_emp;
FETCH c_emp INTO rty_employe;
WHILE (c_emp%FOUND) LOOP
dbms_output.put_line('NO: ' || rty_employe.NO || chr(9) ||
'NOM: ' || rty_employe.NOM || chr(9) || ' SALAIRE:' ||
rty_employe.SALAIRE);
FETCH c_emp INTO rty_employe;
END LOOP;
CLOSE c_emp;
END;
/
24. CURSEURS EXPLICITES
LA BOUCLE FOR
L’utilisation de la boucle FOR dans les curseurs permet
de fournir au programmeur une structure simple et
efficace.
Dans ce cas on parle de curseur semi-automatique.
Si on utilise la boucle FOR dans les curseurs:
On va Éviter les directives OPEN, FETCH et CLOSE,
La boucle FOR s'arrête automatiquement après l’extraction
de la dernière ligne du curseur,
la variable de réception du curseur est automatiquement
déclarée (%ROWTYPE du curseur).
24
25. CURSEURS EXPLICITES
LA BOUCLE FOR
25
DECLARE
CURSOR c_emp IS SELECT *FROM e_emp;
-- rty_employe c_emp%ROWTYPE;
BEGIN
-- OPEN c_emp;
-- FETCH c_emp INTO rty_employe;
FOR rty_employe IN c_emp LOOP
dbms_output.put_line('NO: ' || rty_employe.NO || chr(9) ||
'NOM: ' || rty_employe.NOM || chr(9) || ' SALAIRE:' ||
rty_employe.SALAIRE);
-- FETCH c_emp INTO rty_employe;
END LOOP;
-- CLOSE c_emp;
END;
26. CURSEURS EXPLICITES
LA BOUCLE FOR
26
DECLARE
CURSOR c_emp IS SELECT *FROM e_emp;
BEGIN
FOR rty_employe IN c_emp LOOP
dbms_output.put_line('NO: ' ||
rty_employe.NO || chr(9) || 'NOM: ' || rty_employe.NOM ||
chr(9) || ' SALAIRE:' || rty_employe.SALAIRE);
END LOOP;
END;
DECLARE
CURSOR c_emp IS SELECT *FROM e_emp;
rty_employe e_emp%ROWTYPE;
BEGIN
OPEN c_emp;
FETCH c_emp INTO rty_employe;
WHILE (c_emp%FOUND) LOOP
dbms_output.put_line('NO: ' ||
rty_employe.NO || chr(9) || 'NOM: ' || rty_employe.NOM ||
chr(9) || ' SALAIRE:' || rty_employe.SALAIRE);
FETCH c_emp INTO rty_employe;
END LOOP;
CLOSE c_emp;
END;
/
27. CURSEURS EXPLICITES
LA BOUCLE FOR
On peut même éviter la déclaration du curseur
dans la partie DECLARE:
27
BEGIN
FOR rty_employe IN (SELECT *FROM e_emp) LOOP
dbms_output.put_line('NO: ' || rty_employe.NO || chr(9) || 'NOM: ' ||
rty_employe.NOM || chr(9) || ' SALAIRE:' || rty_employe.SALAIRE);
END LOOP;
END;
28. CURSEURS EXPLICITES
LA BOUCLE FOR
Exercice:
Ecrivez un programme PL/SQL qui permet
d’afficher les enregistrements (champs No, Nom
et Salaire) de la table E_EMP qui ont un salaire
supérieur à la moyenne des salaires des employés
en utilisant la boucle FOR
28
29. CURSEURS EXPLICITES
LA BOUCLE FOR
Solution
29
DECLARE
v_mean float;
BEGIN
select AVG(SALAIRE) into v_mean from E_EMP;
dbms_output.put_line('la moyenne des salaires est: ' || v_mean);
FOR rty_employe IN (SELECT *FROM e_emp where SALAIRE>=
v_mean) LOOP
dbms_output.put_line('NO: ' || rty_employe.NO || chr(9) ||
'NOM: ' || rty_employe.NOM || chr(9) || ' SALAIRE:' ||
rty_employe.SALAIRE);
END LOOP;
END;
30. CURSEURS EXPLICITES
LES CURSEURS PARAMÉTRABLES
Un curseur peut avoir des paramètres en entrée.
Ce passage de paramètre est très utile lorsqu’un
même curseur doit être utilisé plusieurs fois avec des
paramètres différents,
À chaque appel paramétré il faut fermer le curseur,
s’il était déjà utilisé, pour l’ouvrir à nouveau en lui
passant d’autres paramètres (sauf si on utilise
l’instruction FOR ; car le curseur est fermé
automatiquement après chaque passage).
30
31. CURSEURS EXPLICITES
LES CURSEURS PARAMÉTRABLES
Syntaxe:
31
La requête SELECT utilise les paramètres passés par
le curseur,
Les types de données (pour les paramètres du curseur)
possibles sont : CHAR, NUMBER, DATE, BOOLEAN
sans spécifier la longueur.
CURSOR nomcurseur (param1 type, param2 type,...) IS SELECT
la_requête_SELECT ;
32. CURSEURS EXPLICITES
LES CURSEURS PARAMÉTRABLES
Exemple:
32
DECLARE
CURSOR c_emp (v_No e_emp.NO%TYPE) IS SELECT *FROM e_emp
WHERE NO=v_No;
rty_employe e_emp%ROWTYPE;
BEGIN
OPEN c_emp(1);
FETCH c_emp INTO rty_employe;
dbms_output.put_line('NO: ' || rty_employe.NO || chr(9) || 'NOM: '
|| rty_employe.NOM || chr(9) || ' SALAIRE:' || rty_employe.SALAIRE);
CLOSE c_emp;
OPEN c_emp(5);
FETCH c_emp INTO rty_employe;
dbms_output.put_line('NO: ' || rty_employe.NO || chr(9) || 'NOM: '
|| rty_employe.NOM || chr(9) || ' SALAIRE:' || rty_employe.SALAIRE);
CLOSE c_emp;
END;
33. CURSEURS EXPLICITES
ACCÈS CONCURRENTS (FOR UPDATE)
Si on cherche à verrouiller les lignes d’une table
interrogée par un curseur pour les mettre à jour,
sans que d’autres utilisateurs ne les modifie en
même temps, il faut utiliser la clause FOR
UPDATE
Elle est utilisée 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.
33
34. CURSEURS EXPLICITES
ACCÈS CONCURRENTS (FOR UPDATE)
Syntaxe:
34
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
Sans NOWAIT et WAIT, le programme attend que les lignes soient
disponibles.
35. CURSEURS EXPLICITES
ACCÈS CONCURRENTS (FOR UPDATE)
Pour modifier la ligne courante, il faut utiliser la
clause WHERE CURRENT OF nomCurseur
35
UPDATE nom_table SET VALUES … WHERE CURRENT OF nomCurseur;
36. CURSEURS EXPLICITES
ACCÈS CONCURRENTS (FOR UPDATE)
Exemple
36
DECLARE
CURSOR c_emp IS SELECT *FROM e_emp FOR UPDATE OF SALAIRE NOWAIT;
BEGIN
FOR rty_emp IN c_emp LOOP
IF rty_emp.SERVICE_NO=1 THEN
update e_emp set SALAIRE =1000 WHERE CURRENT OF c_emp;
ELSIF rty_emp.SERVICE_NO=2 THEN
update e_emp set SALAIRE =2000 WHERE CURRENT OF c_emp;
ELSE
update e_emp set SALAIRE =3000 WHERE CURRENT OF c_emp;
END IF;
END LOOP;
COMMIT;
FOR rty_emp IN c_emp LOOP
dbms_output.put_line('NO: ' || rty_emp.NO || chr(9) || 'NOM: ' ||
rty_emp.NOM || chr(9) || 'SERVICE_NO: ' || rty_emp.SERVICE_NO|| chr(9) || ' SALAIRE:' ||
rty_emp.SALAIRE);
END LOOP;
END;
/
37. VARIABLES DE TYPES CURSEURS
Une variable curseur (ou référence à un curseur REF
CURSOR) définit un curseur dynamique qui n’est pas
associé à aucune requête fixe (contrairement à un
curseur classique dit statique)
Ainsi, une variable curseur permet au curseur d’évoluer au
cours du programme.
Une variable de type curseur est un type de type REF
CURSOR.
37
38. VARIABLES DE TYPES CURSEURS
Une variable curseur est déclarée en deux étapes:
1. Déclaration du type,
2. Déclaration (instanciation) de la variable du type.
Une variable REF CURSOR peut être définie dans un bloc
PL/SQL par les instructions suivantes:
38
TYPE nomTypeCurseurDynamique IS REF CURSOR [RETURN
typeRetourSQL];
nomCurseurDynamique nomTypeCurseurDynamique;
Le type de retour, qui est facultatif, représente en général la
structure d’un enregistrement d’une table (ROWTYPE)
Dans le cas ou un curseur inclut un type de retour, le curseur
dynamique est dit typé.
Dans le cas inverse, il est non typé et permet une grande
flexibilité car toute requête peut y être associée.
39. VARIABLES DE TYPES CURSEURS
Pour utiliser une variable curseur:
On affecte une requête à cette variable de curseur dans le
corps du programme (bloc),
Après, on peut utiliser cette variable de curseur comme
n'importe quel autre curseur.
L’ouverture d’un curseur dynamique est commandée
par l’instruction OPEN FOR requête,
39
La lecture du curseur s’opère toujours avec l’instruction
FETCH.
La fermeture du curseur s’opère avec l’instruction CLOSE
Pour changer la requête affectée au curseur il faut fermer le
curseur si il est ouvert
OPEN nomTypeCurseurDynamique FOR la_requéte;
40. VARIABLES DE TYPES CURSEURS
CURSEURS NON TYPÉS
40
DECLARE
TYPE ref_product IS REF CURSOR;
c_product ref_product;
var1 T_Product.name%TYPE ;
var2 T_Product.mark%TYPE ;
var3 T_Product.QtyInStock%TYPE ;
BEGIN
OPEN c_product FOR SELECT name, mark FROM T_Product WHERE ID=1;
FETCH c_product INTO var1, var2;
WHILE (c_product%FOUND) LOOP
DBMS_OUTPUT.PUT_LINE('ID=1, nom : ' || var1 || ' , marque: ' || var2 );
FETCH c_product INTO var1, var2;
END LOOP;
IF c_product%ISOPEN THEN
CLOSE c_product;
END IF;
OPEN c_product FOR SELECT QtyInStock FROM T_Product WHERE ID=1;
FETCH c_product INTO var3;
WHILE (c_product%FOUND) LOOP
DBMS_OUTPUT.PUT_LINE('ID=1, QtyInStock: ' || var3);
FETCH c_product INTO var3;
END LOOP;
CLOSE c_product;
END;
41. VARIABLES DE TYPES CURSEURS
CURSEURS TYPÉS
41
DECLARE
TYPE ref_product IS REF CURSOR RETURN T_Product%ROWTYPE;
c_product ref_product;
rty_product T_Product %ROWTYPE ;
BEGIN
OPEN c_product FOR SELECT * FROM T_Product WHERE NOT (ID=1);
FETCH c_product INTO rty_product;
WHILE (c_product%FOUND) LOOP
DBMS_OUTPUT.PUT_LINE('ID: ' || rty_product.ID|| ' , nom : ' ||
rty_product.name|| ' , marque: ' || rty_product.mark|| ' , QtyInStock: ' || rty_product.QtyInStock);
FETCH c_product INTO rty_product;
END LOOP;
IF c_product%ISOPEN THEN
CLOSE c_product;
END IF;
OPEN c_product FOR SELECT * FROM T_Product WHERE ID=1;
FETCH c_product INTO rty_product;
WHILE (c_product%FOUND) LOOP
DBMS_OUTPUT.PUT_LINE('ID: ' || rty_product.ID|| ' , nom : ' ||
rty_product.name|| ' , marque: ' || rty_product.mark|| ' , QtyInStock: ' || rty_product.QtyInStock);
FETCH c_product INTO rty_product;
END LOOP;
CLOSE c_product;
END;
42. VARIABLES DE TYPES CURSEURS
Exercice:
Ecrivez deux programmes PL/SQL, en utilisant:
1. Les curseurs non typés
2. Les curseurs typés.
qui permettent d’augmenter de 5% les salaires des
employés qui ont un salaire inférieur à la moyenne des
salaires.
PS: Il faut faire attention à ce que le salaire inférieur des
employés soit celui d’un employé parmi ceux qui avait un salaire
inférieur à la moyenne.
Afficher le Nom, le salaire avant augmentation et le
salaire après augmentation de ces employés. 42