Pourquoi utiliser des curseurs explicites au lieu de boucles régulières?


12

J'écris des applications Web de base depuis un an (pour une base de données Oracle), et puisque les fonctions sont assez simples, la plupart d'entre nous s'en tiennent à des boucles FOR régulières pour obtenir nos données:

for i in (select * from STUDENTS) loop
      htp.prn(i.student_last_name || ', ' || i.student_first_name || ' ' || i.student_dob);
end loop;

Mais les curseurs semblent être la «bonne» façon de faire les choses. Je peux trouver beaucoup d'informations sur ce que sont les curseurs et les différentes façons de les parcourir, mais je ne trouve pas de raison solide de les utiliser sur des boucles FOR régulières. Cela dépend-il des besoins de la procédure? Y a-t-il des avantages inhérents dont je devrais être conscient?


Ce type de FORest juste une autre façon d'utiliser les curseurs. Voir les documents: docs.oracle.com/cd/E11882_01/appdev.112/e10472/… Quoi qu'il en soit, que fait htp.prn ()?
dezso

C'est l'une de nos fonctions de sortie. Alors, avez-vous une préférence sur la méthode à utiliser?
ini

Pour ce type de choses, la FORboucle est beaucoup plus lisible, je pense. J'ai tendance à utiliser de «vrais» curseurs uniquement si je dois reculer, pas seulement vers l'avant. J'ai posé cette autre question parce que je peux imaginer une fonction de table au lieu de htp.prn().
dezso

Il convient de mentionner que les deux formes de curseur ont des performances inférieures à une solution SQL pure - particulièrement pertinentes pour les instructions DML.
David Aldridge

Réponses:


7

Un curseur peut être explicite ou implicite, et l'un ou l'autre type peut être utilisé dans une boucle FOR. Il y a vraiment deux aspects à votre question.

  1. Pourquoi utiliser une boucle FOR de curseur explicite sur une boucle FOR de curseur implicite?

    • Utilisez une boucle FOR de curseur explicite lorsque la requête sera réutilisée, sinon un curseur implicite est préférable.
  2. Pourquoi utiliser une boucle avec un FETCH plutôt qu'une boucle FOR qui n'a pas de FETCH explicite?

    • Utilisez un FETCH dans une boucle lorsque vous devez effectuer une collecte groupée ou lorsque vous avez besoin de SQL dynamique.

Voici quelques informations utiles de la documentation.

Exemple de curseur implicite pour boucle

BEGIN
   FOR vItems IN (
      SELECT last_name
      FROM employees
      WHERE manager_id > 120
      ORDER BY last_name
   ) 
   LOOP
      DBMS_OUTPUT.PUT_LINE ('Name = ' || vItems.last_name);
   END LOOP;
END;
/

Exemple de curseur explicite pour boucle

DECLARE
   CURSOR c1 IS
      SELECT last_name
      FROM employees
      WHERE manager_id > 120
      ORDER BY last_name;
BEGIN
   FOR vItems IN c1 LOOP
      DBMS_OUTPUT.PUT_LINE ('Name = ' || vItems.last_name);
   END LOOP;
END;
/

Curseur implicite

Un curseur implicite est un curseur de session qui est construit et géré par PL / SQL. PL / SQL ouvre un curseur implicite chaque fois que vous exécutez une instruction SELECT ou DML. Vous ne pouvez pas contrôler un curseur implicite, mais vous pouvez obtenir des informations à partir de ses attributs.

Un curseur implicite se ferme après l'exécution de son instruction associée; cependant, ses valeurs d'attribut restent disponibles jusqu'à ce qu'une autre instruction SELECT ou DML s'exécute.

Les attributs implicites du curseur sont: SQL% ISOPEN, SQL% FOUND, SQL% NOTFOUND, SQL% ROWCOUNT, SQL% BULK_ROWCOUNT, SQL% BULK_EXCEPTIONS

Curseur explicite

Un curseur explicite est un curseur de session que vous construisez et gérez. Vous devez déclarer et définir un curseur explicite, en lui donnant un nom et en l'associant à une requête (généralement, la requête renvoie plusieurs lignes). Ensuite, vous pouvez traiter l'ensemble de résultats de la requête de l'une des manières suivantes:

Ouvrez le curseur explicite (avec l'instruction OPEN), récupérez les lignes du jeu de résultats (avec l'instruction FETCH) et fermez le curseur explicite (avec l'instruction CLOSE).

Utilisez le curseur explicite dans une instruction curseur FOR LOOP (voir «Traitement du jeu de résultats de requête avec les instructions FOR LOOP du curseur»).

Vous ne pouvez pas affecter une valeur à un curseur explicite, l'utiliser dans une expression ou l'utiliser comme paramètre de sous-programme formel ou variable hôte. Vous pouvez faire ces choses avec une variable de curseur (voir "Variables de curseur").

Contrairement à un curseur implicite, vous pouvez référencer un curseur explicite ou une variable de curseur par son nom. Par conséquent, un curseur explicite ou une variable de curseur est appelé curseur nommé.

Instructions Cursor FOR LOOP

L'instruction cursor FOR LOOP vous permet d'exécuter une instruction SELECT, puis de parcourir immédiatement les lignes de l'ensemble de résultats. Cette instruction peut utiliser un curseur implicite ou explicite.


1
Les curseurs implicites récupèrent 100 lignes à la fois à partir de 10g.
David Aldridge

16

Le code que vous avez publié utilise un curseur. Il utilise une boucle de curseur implicite.

Dans certains cas, l'utilisation d'une boucle de curseur explicite (c'est-à-dire la déclaration d'une variable CURSOR dans la section déclaration) produit un code plus propre ou de meilleures performances

  1. Si vous avez des requêtes plus complexes que vous ne pouvez pas refactoriser en vues, cela peut rendre le code plus facile à lire si votre boucle itère sur student_cursorplutôt que d'inclure une instruction SQL de 30 lignes qui intègre un tas de logique. Par exemple, si vous imprimiez tous les étudiants qui ont été autorisés à obtenir leur diplôme et qui impliquaient de rejoindre des tables qui avaient leurs dossiers universitaires, les exigences de leur programme d'études, des tables contenant des informations sur les réservations académiques, des tables contenant des informations sur les livres de bibliothèque en retard, des tableaux contenant des informations sur les frais impayés, les remplacements administratifs, etc., il serait probablement judicieux de refactoriser le code afin que cette requête ne soit pas coincée au milieu du code qui concerne la présentation de la liste à un utilisateur. Cela pourrait impliquer la création d'une vue qui résumerait toute cette logique. Ou cela peut impliquer la création d'un curseur explicite qui a été déclaré comme faisant partie du bloc PL / SQL actuel ou dans un bloc PL / SQL de niveau supérieur (c'est-à-dire un curseur déclaré dans un package) afin qu'il soit réutilisable. Ou cela peut impliquer de faire autre chose pour l'encapsulation et la réutilisabilité (par exemple, la création d'une fonction de table en pipeline à la place).
  2. Si vous souhaitez utiliser des opérations groupées en PL / SQL, vous souhaitez généralement utiliser des curseurs explicites. Voici un thread StackOverflow qui traite des différences de performances entre les curseurs explicites et implicites . Si tout ce que vous faites est d'appeler htp.prn, faire BULK COLLECTprobablement ne vous rapporte rien. Dans d'autres cas, cependant, cela peut entraîner des améliorations substantielles des performances.

2

Je vois que de nombreux développeurs utilisent des curseurs explicites au lieu de curseurs implicites par habitude. En effet, dans Oracle version 7, c'était toujours la manière la plus efficace de procéder. De nos jours, il y a généralement l'inverse. Surtout avec l'optimiseur qui, si nécessaire, peut réécrire le curseur implicite pour les boucles dans une collecte en bloc.


0

Récemment, j'ai dû réécrire un tas de requêtes à partir d'une boucle FOR implicite dans des curseurs explicites. La raison en était que les requêtes récupéraient des données d'une base de données externe via un lien et que cette base de données avait un encodage différent de notre base de données locale. Lors du transfert de données du curseur implicite vers un type d'enregistrement défini localement, de mystérieuses erreurs intermittentes se sont produites (uniquement sur certaines lignes spécifiques). Notre DBA nous a expliqué cela, nous n'aurions pas pu aller au fond des choses nous-mêmes. Il semble que ce soit un bug dans Oracle qui a été signalé.

On nous a conseillé de tout réécrire à l'aide de curseurs explicites et l'erreur a disparu.

Ce n'est pas la principale raison pour laquelle vous voudrez peut-être utiliser explicite plutôt qu'implicite, mais cela vaut la peine d'être noté.

EDIT: Oracle 12c.


Pourriez-vous ajouter le numéro de bogue et / ou de note afin que ceux qui lisent ceci puissent en savoir plus sur les symptômes et si / quand cela est résolu?
Leigh Riffel

Désolé, le rapport de bogue a été fait par l'un de nos administrateurs de base de données, je n'ai pas accès à ces informations.
Robotron
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.