web-dev-qa-db-fra.com

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

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 peux pas trouver une raison solide pour 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?

12
ini

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 avez besoin de collecter en bloc 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 de curseur implicites 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é.

Curseur pour les instructions 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.

7
Leigh Riffel

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_cursor plutôt que d'inclure une instruction SQL de 30 lignes qui incorpore 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 scolaires, les exigences de leur programme d'études, des tableaux contenant des informations sur les réservations académiques, des tableaux 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, faisant un BULK COLLECT ne vous achète probablement rien. Dans d'autres cas, cependant, cela peut entraîner des améliorations substantielles des performances.
16
Justin Cave

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.

2
Peter Åkerlund

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.

0
Robotron