
Les CURSORS (ou curseurs en français) sont des boucles qu’on utilise pour faire des itérations sur le résultat d’une requête SELECT ligne par ligne.
Exemple, on peut créer un curseur qui va boucler sur la table EMP avec le PRENOM commençant par B. Pour chaque ligne de ce résultat, on va afficher les informations de cet employé.
Deux Type de curseurs existent :
1 . Les curseurs explicites:
🧪 Envie de pratiquer ? Exécutez les exemples de cet article dans notre SQL Playground gratuit — aucun logiciel à installer.
Les curseurs explicites sont déclarés dans la partie de déclaration du Bloc PL/SQL :
DECLARE
CURSOR <nom_du_curseur> IS
SELECT ... FROM ... ;
BEGIN
...
END;
Exemple : Un curseur qui comporte tous les employés de la table EMP qui occupent un poste de MANAGER
DECLARE
CURSOR curseur_EMPLOYE IS
SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER';
BEGIN
...
END;Une fois le curseur déclaré, on pourra l’utiliser dans le Bloc PL/SQL après le BEGIN. On commence d’abord par l’ouvrir avant de la parcourir.
Pour ouvrir un curseur :
DECLARE CURSOR curseur_EMPLOYE IS SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER'; BEGIN OPEN curseur_EMPLOYE ; ... END;
Après l’ouverture du curseur, nous pouvons le parcourir et faire autant d’itération possible que de lignes retournées par la requête du curseur. On utilise la boucle LOOP … END LOOP pour les itérations :
DECLARE CURSOR curseur_EMPLOYE IS SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER'; BEGIN OPEN curseur_EMPLOYE ; LOOP ... END LOOP; END;
Une fois nous somme dans la boucle, on doit affecter la ligne en cours à une ou des variables selon les colonnes sélectionnées dans notre SELECT. Pour cela on utilise FETCH … INTO :
DECLARE V_ENAME EMP.ENAME%TYPE; V_MGR EMP.MGR%TYPE; V_SAL EMP.SAL%TYPE; CURSOR curseur_EMPLOYE IS SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER'; BEGIN OPEN curseur_EMPLOYE ; LOOP FETCH curseur_EMPLOYE INTO V_ENAME, V_MGR, V_SAL; ... END LOOP; END;
Enfin, il faudra ajouter la condition de sortie de la boucle. Généralement la condition de sortie est que nous avons parcouru toutes les lignes retournées :
DECLARE V_ENAME EMP.ENAME%TYPE; V_MGR EMP.MGR%TYPE; V_SAL EMP.SAL%TYPE; CURSOR curseur_EMPLOYE IS SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER'; BEGIN OPEN curseur_EMPLOYE ; LOOP FETCH curseur_EMPLOYE INTO V_ENAME, V_MGR, V_SAL; ... EXIT WHEN NOT curseur_EMPLOYE%FOUND; /* On peut utiliser aussi NOTFOUND comme suit EXIT WHEN curseur_EMPLOYE%NOTFOUND; */
END LOOP;
END;Le curseur est mis en place, nous pouvons exploiter et manipuler ses données selon notre besoin puis fermer ce curseur :
DECLARE
V_ENAME EMP.ENAME%TYPE;
V_MGR EMP.MGR%TYPE;
V_SAL EMP.SAL%TYPE;
CURSOR curseur_EMPLOYE IS
SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER';
BEGIN
OPEN curseur_EMPLOYE ;
LOOP
FETCH curseur_EMPLOYE INTO V_ENAME, V_MGR, V_SAL;
DBMS_OUTPUT.PUT_LINE('L''employé ' || V_ENAME || ' a un salaire de ' || V_SAL ||
'. Le numéro de son Manager est ' || V_MGR);
EXIT WHEN NOT curseur_EMPLOYE%FOUND;
END LOOP;
CLOSE curseur_EMPLOYE ;
END;Le résultat de ce Block PL/SQL est le suivant :
L'employé JONES a un salaire de 2975. Le numéro de son Manager est 7839
L'employé BLAKE a un salaire de 2850. Le numéro de son Manager est 7839
L'employé CLARK a un salaire de 2450. Le numéro de son Manager est 7839Nous pouvons utiliser * dans le SELECT du curseur, dans ce cas, la déclaration de la variable qui comportera les lignes de curseur sera du même type que la table sélectionnée ROWTYPE. Par-contre l’appel de chaque colonne sera <nom_curseur>.<nom_colonne> :
DECLARE
V_EMP EMP%ROWTYPE;
CURSOR curseur_EMPLOYE IS
SELECT * FROM EMP WHERE JOB = 'MANAGER';
BEGIN
OPEN curseur_EMPLOYE ;
LOOP
FETCH curseur_EMPLOYE INTO V_EMP ;
DBMS_OUTPUT.PUT_LINE('L''employé ' || V_EMP.ENAME || ' a un salaire de ' ||
V_EMP.SAL || '. Le numéro de son Manager est ' || V_EMP.MGR);
EXIT WHEN NOT curseur_EMPLOYE%FOUND;
END LOOP;
CLOSE curseur_EMPLOYE ;
END;Le deuxième type de curseur est implicite
2 . Les curseurs implicites:
Les curseurs implicites sont plus facile à utiliser que ceux explicites. On ne déclare pas de curseur, ni on l’ouvre ni on le ferme. On utilise toujours la boucle FOR :
FOR <nom_curseur> IN (<requte_SELECT>)
LOOP
...
END LOOP;On reprend le dernier exemple cette fois ci avec un curseur explicite :
DECLARE
BEGIN
FOR curseur_EMPLOYE IN (SELECT * FROM EMP WHERE JOB = 'MANAGER')
LOOP
DBMS_OUTPUT.PUT_LINE('L''employé ' || curseur_EMPLOYE.ENAME ||
' a un salaire de ' || curseur_EMPLOYE.SAL ||
'. Le numéro de son Manager est ' || curseur_EMPLOYE.MGR);
END LOOP;
END;Le résultat est toujours le même dans ce cas:
L'employé JONES a un salaire de 2975. Le numéro de son Manager est 7839
L'employé BLAKE a un salaire de 2850. Le numéro de son Manager est 7839
L'employé CLARK a un salaire de 2450. Le numéro de son Manager est 7839
Qu’est-ce qu’un curseur en PL/SQL Oracle ?
Un curseur est un pointeur vers la zone mémoire qui contient le résultat d'une requête. Il permet de parcourir un résultat ligne par ligne, ce que le SQL seul ne sait pas faire.
| Type de curseur | Qui le déclare | Quand l’utiliser |
|---|---|---|
| Implicite | Oracle, automatiquement | tout SELECT INTO, INSERT, UPDATE, DELETE |
| Explicite | Le développeur | parcourir plusieurs lignes avec contrôle complet |
| Curseur FOR LOOP | Le développeur, forme courte | cas le plus fréquent — le plus lisible |
| REF CURSOR | Le développeur | renvoyer un résultat à une application externe |
Syntaxe du curseur explicite
Quatre étapes : déclarer, ouvrir, lire, fermer.
DECLARE
CURSOR c_emp IS
SELECT nom, salaire FROM employes WHERE departement = 10;
v_nom employes.nom%TYPE;
v_salaire employes.salaire%TYPE;
BEGIN
OPEN c_emp;
LOOP
FETCH c_emp INTO v_nom, v_salaire;
EXIT WHEN c_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_nom || ' : ' || v_salaire);
END LOOP;
CLOSE c_emp;
END;
/La boucle FOR : la forme à privilégier
Le curseur FOR LOOP fait tout à votre place : déclaration de la variable, OPEN, FETCH, test de fin et CLOSE. C'est la forme recommandée dans 90 % des cas.
BEGIN
FOR r IN (SELECT nom, salaire FROM employes WHERE departement = 10) LOOP
DBMS_OUTPUT.PUT_LINE(r.nom || ' : ' || r.salaire);
END LOOP;
END;
/Curseur paramétré
Un curseur peut recevoir des paramètres, ce qui évite de le dupliquer :
DECLARE
CURSOR c_emp(p_dept NUMBER) IS
SELECT nom FROM employes WHERE departement = p_dept;
BEGIN
FOR r IN c_emp(10) LOOP
DBMS_OUTPUT.PUT_LINE(r.nom);
END LOOP;
END;
/Les attributs de curseur
| Attribut | Renvoie | Usage typique |
|---|---|---|
%FOUND | TRUE si le dernier FETCH a ramené une ligne | boucles WHILE |
%NOTFOUND | TRUE si plus aucune ligne | EXIT WHEN c%NOTFOUND |
%ROWCOUNT | Nombre de lignes lues jusqu'ici | limiter le traitement |
%ISOPEN | TRUE si le curseur est ouvert | éviter ORA-06511 |
Ces attributs existent aussi sur le curseur implicite via SQL%ROWCOUNT, très utile après un UPDATE :
BEGIN
UPDATE employes SET salaire = salaire * 1.05 WHERE departement = 10;
DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT || ' lignes mises à jour');
END;
/Erreurs fréquentes avec les curseurs
- ORA-01001 : invalid cursor — vous faites un
FETCHsur un curseur non ouvert ou déjà fermé. - ORA-06511 : cursor already open — double
OPEN. Testez%ISOPENavant. - Boucle infinie —
EXIT WHENplacé avant leFETCHau lieu d'après. - Performance — un curseur qui fait un
UPDATEligne par ligne est très lent. Préférez unUPDATEensembliste, ouBULK COLLECT+FORALLpour les gros volumes.
Sur le même thème
- Cours SQL ORACLE – 00 : Introduction aux objets d’une BD Oracle
- Cours SQL ORACLE – 01 : Les Tables (CREATE ALTER TABLE).
- Cours SQL ORACLE – 02 : Les requêtes DML (SELECT, INSERT, UPDATE et DELETE).
- Cours SQL ORACLE – 03 : Les jointures (LEFT RIGHT FULL JOIN).
- Cours SQL ORACLE – 04 : Les fonctions d’agrégation
- Cours SQL ORACLE – 05 : GROUP BY HAVING
