Cours SQL ORACLE – 07-04 : CURSOR (Curseur)

Illustration du tutoriel SQL Oracle : Cours SQL ORACLE – 07-04 : CURSOR (Curseur)

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.

Publicité

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 7839

Nous 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 curseurQui le déclareQuand l’utiliser
ImpliciteOracle, automatiquementtout SELECT INTO, INSERT, UPDATE, DELETE
ExpliciteLe développeurparcourir plusieurs lignes avec contrôle complet
Curseur FOR LOOPLe développeur, forme courtecas le plus fréquent — le plus lisible
REF CURSORLe développeurrenvoyer 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;
/
Publicité

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

AttributRenvoieUsage typique
%FOUNDTRUE si le dernier FETCH a ramené une ligneboucles WHILE
%NOTFOUNDTRUE si plus aucune ligneEXIT WHEN c%NOTFOUND
%ROWCOUNTNombre de lignes lues jusqu'icilimiter le traitement
%ISOPENTRUE 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 FETCH sur un curseur non ouvert ou déjà fermé.
  • ORA-06511 : cursor already open — double OPEN. Testez %ISOPEN avant.
  • Boucle infinieEXIT WHEN placé avant le FETCH au lieu d'après.
  • Performance — un curseur qui fait un UPDATE ligne par ligne est très lent. Préférez un UPDATE ensembliste, ou BULK COLLECT + FORALL pour les gros volumes.

Sur le même thème

Un nouveau tutoriel SQL par semaine

Nous ne spammons pas ! Consultez notre politique de confidentialité pour plus d’informations.

Publicité

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Cours SQL & Oracle — 100% gratuitVoir les cours →