Vous êtes sur la page 1sur 6

Les curseurs explicites

La technique du curseur déclaré explicitement permet de parcourir une à une les


lignes du résultat d'une requête.
L'intérêt du curseur est donc de pouvoir récupérer (par un SELECT) plusieurs
lignes, ce que ne permet pas le SELECT normal en PL/SQL (voir sql.html).

1. Schéma d'ensemble

1.1. Fonctionnement

1. On déclare le curseur (avec son SELECT)


2. On l'ouvre (on n'est pas obligé de l'ouvrir tout de suite)
3. On en récupère les lignes une à une, en commençant par la première
4. On le referme

1.2. Syntaxe

1.2.1. Déclaration :

CURSOR <nom_du_curseur> IS
SELECT ... ;

Le SELECT est ici identique au SELECT de SQL. L'utilisation d'une instruction


ORDER BY peut être utile pour les traitements suivants.

1.2.2. Ouverture :

OPEN <nom_du_curseur> ;

À l'issue de cette ouverture du curseur, on a une ou plusieurs lignes de données


accessibles.
On peut ouvrir plusieurs curseurs en même temps, mais il y a une limite fixée
par la constante OPEN_CURSORS (valeur par défaut : 50).

1.2.3. Récupération des lignes dans des variables :

FETCH <nom_du_curseur> INTO <variable1>,<variable2> ;

Il doit y avoir autant de variables qu'on a de champs interrogés par le SELECT.

Récupération des lignes dans un enregistrement :

FETCH <nom_du_curseur> INTO <enregistrement> ;


Un enregistrement est une variable spéciale permettant de stocker une ligne de
plusieurs champs. Il faut l'avoir d'abord déclaré comme ceci :
maligne moncurseur%TYPE ; où moncurseur est le nom du curseur.

Il existe d'autres types d'enregistrement : RECORD, TABLE, "nested TABLE" et


VARRAY

Quand on ouvre le curseur, c'est la première ligne qui est pointée. Ensuite, à
chaque FETCH, on passe à la ligne suivante. Pour savoir combien de lignes on a
déjà récupérées, il faut utiliser l'attribut %ROWCOUNT.

1.2.4. Fermeture :

CLOSE <nom_du_curseur> ;

1.3. Exemple

Voici un script qui renvoie toutes les DORIS_KEY de la table BUDGET pour les
budgets de l'exercice 2007 :

DECLARE
CURSOR mesbudgets IS
SELECT * FROM BUDGET
WHERE EXERCICE = '2007';
mon_budget BUDGET%ROWTYPE;
BEGIN
OPEN mesbudgets;
LOOP
FETCH mesbudgets INTO mon_budget;
DBMS_OUTPUT.PUT_LINE(mon_budget.DORIS_KEY);
EXIT WHEN mesbudgets%NOTFOUND;
END LOOP;
CLOSE mesbudgets;
END;

2. Les attributs de curseur

2.1. Définitions

On peut récupérer des informations sur un curseur par l'intermédiaire des


attributs suivants :
<nom_du_curseur>%ISOPEN : retourne TRUE ou FALSE.
<nom_du_curseur>%FOUND : retourne TRUE ou FALSE suivant que le dernier
FETCH a retourné une ligne ou non.
<nom_du_curseur>%NOTFOUND : retourne TRUE ou FALSE à l'inverse de %FOUND
<nom_du_curseur>%ROWCOUNT : retourne le nombre de FETCH ayant renvoyé une
ligne. Avant le premier FETCH, cet attribut renvoie 0.

2.2. Exemples

%ISOPEN
IF NOT moncurseur%ISOPEN THEN
OPEN moncurseur;
END IF;

%FOUND

OPEN moncurseur;
LOOP
FETCH moncurseur INTO variable1,variable2;
EXIT WHEN NOT moncurseur%FOUND;
END LOOP;
CLOSE moncurseur;

Avec un WHILE :

OPEN moncurseur;
FETCH moncurseur INTO variable1,variable2;
WHILE moncurseur%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(TO_CHAR(variable1)||TO_CHAR(variable2));
FETCH moncurseur INTO variable1,variable2;
END LOOP;
CLOSE moncurseur;

%NOTFOUND

OPEN moncurseur;
LOOP
FETCH moncurseur INTO variable1,variable2;
EXIT WHEN moncurseur%NOTFOUND;
END LOOP;
CLOSE moncurseur;

%ROWCOUNT

SET SERVEROUTPUT ON
DECLARE
CURSOR moncurseur IS
SELECT champ1,champ2 FROM table;
variable1 table.champ1%TYPE;
variable2 table.champ2%TYPE;
BEGIN
OPEN moncurseur;
LOOP
FETCH moncurseur INTO variable1,variable2;
EXIT WHEN NOT moncurseur%FOUND;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(moncurseur%ROWCOUNT));
CLOSE moncurseur;
END;

3. Déclarations implicites avec FOR ... IN ... LOOP

3.1. Déclarations implicites des OPEN, FETCH et CLOSE


Il existe une syntaxe qui permet de se passer des instructions OPEN, FETCH et
CLOSE :

DECLARE
CURSOR moncurseur IS
SELECT champ1,champ2 FROM table;
variable1 table.champ1%TYPE;
variable2 table.champ2%TYPE;
BEGIN
FOR maligne IN moncurseur LOOP
...
END LOOP;
END;

Dans cette structure, beaucoup plus rapide, même la variable maligne, qui
contient la ligne récupérée du curseur, n'a pas besoin d'être déclarée avant et
est détruite à la sortie de la boucle.

3.2. Déclarations implicites du curseur

On peut même se passer de la déclaration du nom du curseur dans la structure


suivante :

DECLARE
variable1 table.champ1%TYPE;
variable2 table.champ2%TYPE;
BEGIN
FOR maligne IN (SELECT champ1,champ2 FROM table) LOOP
...
END LOOP;
END;

Le curseur est créé dans la sous-requête IN (SELECT...).

4. FOR UPDATE

Si l'on utilise un curseur pour faire des mises à jour, il est préférable d'utiliser la
syntaxe suivante :

DECLARE
CURSOR moncurseur IS
SELECT champ1,champ2 FROM table
FOR UPDATE;

Cette déclaration verrouille les lignes de la table sélectionnée. Personne d'autre


ne pourra mettre à jour ces lignes pendant le traitement de cette procédure.
On n'est pas obligé de verrouiller tous les champs de la ligne. On peut choisir les
champs comme ceci :

DECLARE
CURSOR moncurseur IS
SELECT champ1,champ2 FROM table
FOR UPDATE OF champ1,champ2;
Dans cette structure, cependant, si quelqu'un d'autre a déjà verrouillé les mêmes
lignes, il faudra attendre que l'autre libère le verrou pour que les lignes puissent
être mises à jour.

5. WHERE CURRENT OF moncurseur

La clause WHERE CURRENT OF <nom_du_curseur> est utile pour les UPDATE et les
DELETE : elle permet de préciser que la mise à jour ou la destruction s'applique à
la ligne en cours récupérée par le curseur cité : pas besoin d'une condition
WHERE sur la valeur d'un champ.

Exemple de UPDATE :

DECLARE
CURSOR moncurseur IS
SELECT NUMERO FROM FASCICULE
WHERE ABONNEMENT = 2500
FOR UPDATE;
BEGIN
FOR monfascicule IN moncurseur LOOP
UPDATE FASCICULE
SET NUMERO = NUMERO + 1
WHERE CURRENT OF moncurseur;
END LOOP;
COMMIT;
END;

Ce programme récupère le champ NUMERO de la table des fascicules, pour les


fascicules liés à l'abonnement 2500. Il met à jour ce champ pour chaque
fascicule.

6. Passer des paramètres à un curseur

Pour pouvoir passer un paramètre à un curseur, il faut le déclarer comme ceci :

DECLARE
CURSOR moncurseur (param table.champ3%TYPE) IS
SELECT champ1,champ2 FROM table
WHERE champ3 = param;

Cela permet d'ouvrir plusieurs curseurs avec un paramètre à chaque fois


différent. On appelle le curseur comme ceci :

BEGIN
OPEN moncurseur(valeur);
...

Où valeur est une valeur du type de champ3. Par exemple : OPEN


moncurseur(10) ou OPEN moncurseur('911.2 ARG')
valeur peut aussi être une valeur entrée au clavier : OPEN
moncurseur(&parametre)
Et valeur peut être aussi calculée à la volée par une expression : OPEN
moncurseur(MOD(variable1,variable2)) (MOD(a,b) renvoie le reste de la
division a/b.)

On peut donner au paramètre une valeur par défaut :

DECLARE
CURSOR moncurseur (param table.champ3%TYPE := 10) IS
...

Et on peut déclarer un curseur avec plusieurs paramètres :

DECLARE
CURSOR moncurseur (param1 table.champ3%TYPE, param2 VARCHAR2) IS
...