¿Por qué usar cursores explícitos en lugar de bucles regulares?

12

He estado escribiendo aplicaciones web básicas durante un año (para una base de datos Oracle), y dado que las funciones son bastante simples, la mayoría de nosotros seguimos con bucles FOR regulares para obtener nuestros datos:

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

Pero, los cursores parecen ser la forma "correcta" de hacer las cosas. Puedo encontrar mucha información sobre qué son los cursores y las diferentes formas de recorrerlos, pero no puedo encontrar una razón sólida por la que usarlos en bucles FOR regulares. ¿Depende de las necesidades del procedimiento? ¿Hay ventajas inherentes que debo tener en cuenta?

ini
fuente
Este tipo de FORes solo otra forma de usar cursores. Consulte los documentos: docs.oracle.com/cd/E11882_01/appdev.112/e10472/… De todos modos, ¿qué hace htp.prn ()?
dezso
Esa es una de nuestras funciones de salida. Entonces, ¿tiene alguna preferencia sobre qué método usar?
ini
Para este tipo de cosas, el FORbucle es mucho más legible, creo. Tiendo a usar cursores 'reales' solo si tengo que dar un paso atrás, no solo hacia adelante. Hice esa otra pregunta porque puedo imaginar una función de tabla en lugar de htp.prn().
dezso
Vale la pena mencionar que ambas formas de cursor tienen un rendimiento inferior a una solución SQL pura, particularmente relevante para las declaraciones DML.
David Aldridge

Respuestas:

7

Un cursor puede ser explícito o implícito, y cualquier tipo puede usarse en un bucle FOR. Realmente hay dos aspectos para su pregunta.

  1. ¿Por qué usar un cursor FOR en bucle explícito sobre un cursor FOR en bucle implícito?

    • Utilice un cursor FOR para el bucle explícito cuando se reutilice la consulta; de lo contrario, se prefiere un cursor implícito.
  2. ¿Por qué usar un bucle con un FETCH en lugar de un bucle FOR que no tiene un FETCH explícito?

    • Use un FETCH dentro de un bucle cuando necesite recopilar en masa o cuando necesite SQL dinámico.

Aquí hay información útil de la documentación.

Ejemplo de cursor implícito FOR LOOP

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;
/

Ejemplo de cursor explícito FOR LOOP

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;
/

Cursor implícito

Un cursor implícito es un cursor de sesión construido y administrado por PL / SQL. PL / SQL abre un cursor implícito cada vez que ejecuta una instrucción SELECT o DML. No puede controlar un cursor implícito, pero puede obtener información de sus atributos.

Un cursor implícito se cierra después de que se ejecuta su instrucción asociada; sin embargo, sus valores de atributo permanecen disponibles hasta que se ejecute otra instrucción SELECT o DML.

Los atributos implícitos del cursor son: SQL% ISOPEN, SQL% FOUND, SQL% NOTFOUND, SQL% ROWCOUNT, SQL% BULK_ROWCOUNT, SQL% BULK_EXCEPTIONS

Cursor explícito

Un cursor explícito es un cursor de sesión que construye y administra. Debe declarar y definir un cursor explícito, asignándole un nombre y asociándolo con una consulta (normalmente, la consulta devuelve varias filas). Luego puede procesar el conjunto de resultados de la consulta de cualquiera de estas formas:

Abra el cursor explícito (con la instrucción OPEN), obtenga filas del conjunto de resultados (con la instrucción FETCH) y cierre el cursor explícito (con la instrucción CLOSE).

Use el cursor explícito en una instrucción de cursor FOR LOOP (consulte "Procesamiento de conjuntos de resultados de consultas con cursor para declaraciones FOR LOOP").

No puede asignar un valor a un cursor explícito, usarlo en una expresión o usarlo como un parámetro de subprograma formal o variable de host. Puede hacer esas cosas con una variable de cursor (consulte "Variables de cursor").

A diferencia de un cursor implícito, puede hacer referencia a un cursor explícito o variable de cursor por su nombre. Por lo tanto, un cursor explícito o una variable de cursor se denomina cursor con nombre.

Cursor para declaraciones LOOP

La instrucción cursor FOR LOOP le permite ejecutar una instrucción SELECT e inmediatamente recorrer las filas del conjunto de resultados. Esta declaración puede usar un cursor implícito o explícito.

Leigh Riffel
fuente
1
Los cursores implícitos obtienen 100 filas a la vez desde 10 g en adelante.
David Aldridge
16

El código que publicaste está usando un cursor. Está utilizando un bucle de cursor implícito.

Hay casos en los que el uso de un bucle de cursor explícito (es decir, declarar una variable CURSOR en la sección de declaración) produce un código más limpio o un mejor rendimiento

  1. Si tiene consultas más complejas que no puede refactorizar en vistas, puede hacer que el código sea más fácil de leer si su ciclo se repitestudent_cursoren lugar de incluir una instrucción SQL de 30 líneas que incorpora un montón de lógica. Por ejemplo, si estaba imprimiendo a todos los estudiantes que se autorizaron a graduarse y que implicaban unirse a tablas que tenían sus registros académicos, los requisitos de su programa de estudios, tablas con información sobre retenciones académicas, tablas con información sobre libros de la biblioteca vencidos, tablas con información sobre tarifas no pagadas, anulaciones administrativas, etc. Probablemente tendría sentido refactorizar el código para que esta consulta no se atasque en el medio del código que se ocupa de presentar la lista a un usuario. Eso podría implicar la creación de una vista que encapsule toda esta lógica. O podría implicar la creación de un cursor explícito que se declaró como parte del bloque PL / SQL actual o en algún bloque PL / SQL de nivel superior (es decir, un cursor declarado en un paquete) para que sea reutilizable. O puede implicar hacer algo más para encapsular y reutilizar (por ejemplo, crear una función de tabla canalizada).
  2. Si desea utilizar operaciones masivas en PL / SQL, generalmente desea utilizar cursores explícitos. Aquí hay un hilo StackOverflow que analiza las diferencias de rendimiento entre los cursores explícitos e implícitos . Si todo lo que está haciendo es llamar htp.prn, BULK COLLECTprobablemente no le compre nada. En otros casos, sin embargo, puede resultar en mejoras sustanciales de rendimiento.
Justin Cave
fuente
2

Veo que muchos desarrolladores están usando cursores explícitos en lugar de cursores implícitos por viejo hábito. Esto porque en la versión 7 de Oracle, esta siempre era la forma más eficiente de hacerlo. Hoy en día generalmente hay al revés. Especialmente con el optimizador que, si es necesario, puede reescribir el cursor implícito para bucles en una recopilación masiva.

Peter Åkerlund
fuente
0

Recientemente tuve que reescribir un montón de consultas desde un bucle FOR implícito en cursores explícitos. La razón fue que las consultas obtuvieron datos de una base de datos externa a través de un enlace y esta base de datos tenía una codificación diferente a nuestra base de datos local. Al transferir datos desde el cursor implícito a un tipo de registro definido localmente, se produjeron misteriosos errores intermitentes (solo en ciertas filas específicas). Nuestro DBA nos explicó esto, no hubiéramos podido llegar al fondo de esto nosotros mismos. Parece que este es un error en Oracle que se ha informado.

Nos aconsejaron reescribir todo usando cursores explícitos y el error desapareció.

No es la razón principal por la que puede querer usar explícito sobre implícito, pero vale la pena tenerlo en cuenta.

EDITAR: Oracle 12c.

Robotron
fuente
¿Podría agregar el número de error y / o nota para que quienes lo lean puedan saber más acerca de los síntomas y si esto se resuelve?
Leigh Riffel
Lo sentimos, el informe de errores fue realizado por uno de nuestros DBA, no tengo acceso a esa información.
Robotron