Cursores implícitos vs Explícitos

Cursores implícitos vs Explícitos

Durante mucho tiempo ha habido debates sobre los méritos relativos de los cursores implícitos y explícitos. La respuesta corta es que los cursores implícitos son más rápidos y resultan en código mucho más limpio por lo que hay muy pocos casos en los que es necesario recurrir a cursores explícitos.

El script p_comparacion_cursor.sql crea un procedimiento que compara la diferencia de rendimiento entre los dos enfoques mediante la realización de varias consultas en la tabla dual.

CREATE OR REPLACE PROCEDURE P_COMPARACION_CURSOR AS

  L_LOOPS NUMBER := 10000;
  L_DUMMY DUAL.DUMMY%TYPE;
  L_START NUMBER;

  CURSOR C_DUAL IS
    SELECT DUMMY 
      FROM DUAL;

BEGIN

  -- Tiempo cursor explícito.
  L_START := DBMS_UTILITY.GET_TIME;
  FOR I IN 1 .. L_LOOPS LOOP
    OPEN C_DUAL;
    FETCH C_DUAL
      INTO L_DUMMY;
    CLOSE C_DUAL;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('Explícito: ' || (DBMS_UTILITY.GET_TIME - L_START));

  -- Tiempo implícito cursor.

  L_START := DBMS_UTILITY.GET_TIME;

  FOR I IN 1 .. L_LOOPS LOOP
    SELECT DUMMY 
      INTO L_DUMMY 
      FROM DUAL;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('Implícito: ' || (DBMS_UTILITY.GET_TIME - L_START));

END P_COMPARACION_CURSOR;

La salida del procedimiento demuestra claramente que los cursores implícitos son más rápidos que los cursores explícitos.

SQL> SET SERVEROUTPUT ON
SQL> EXEC P_COMPARACION_CURSOR

Explícito: 44
Implícito: 36

PL/SQL procedure successfully completed

Lo interesante es que el cursor implícito no sólo es más rápido, sino que en realidad está haciendo más trabajo, ya que incluye una NO_DATA_FOUND y una TOO_MANY_ROWS verificación de excepción. Para que sean equivalentes deberíamos codificar el cursor explícito como el que se muestra en el script p_verdadera_comparacion_cursor.sql

CREATE OR REPLACE PROCEDURE P_VERDADERA_COMPARACION_CURSOR AS

  L_LOOPS NUMBER := 10000;
  L_DUMMY DUAL.DUMMY%TYPE;
  L_START NUMBER;

  CURSOR C_DUAL IS
    SELECT DUMMY 
      FROM DUAL;

BEGIN

  -- Tiempo cursor explícito.
  L_START := DBMS_UTILITY.GET_TIME;
  FOR I IN 1 .. L_LOOPS LOOP

    OPEN C_DUAL;
    FETCH C_DUAL
      INTO L_DUMMY;

    IF (C_DUAL%NOTFOUND) THEN
      RAISE NO_DATA_FOUND;
    END IF;

    FETCH C_DUAL
      INTO L_DUMMY;

    IF (C_DUAL%FOUND) THEN
      RAISE TOO_MANY_ROWS;
    END IF;

    CLOSE C_DUAL;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('Explicit: ' || (DBMS_UTILITY.GET_TIME - L_START));

  -- Tiempo implícito cursor.

  L_START := DBMS_UTILITY.GET_TIME;
  FOR I IN 1 .. L_LOOPS LOOP
    SELECT DUMMY 
      INTO L_DUMMY 
      FROM DUAL;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('Implicit: ' || (DBMS_UTILITY.GET_TIME - L_START));

END P_VERDADERA_COMPARACION_CURSOR;

La salida de este procedimiento muestra una discrepancia de velocidad aún mayor.

SQL> SET SERVEROUTPUT ON
SQL> EXEC P_VERDADERA_COMPARACION_CURSOR;

Explicit: 272
Implicit: 36

PL/SQL procedure successfully completed

Puesto que ambos cursores ahora están haciendo la misma cantidad de trabajo ¿por qué hay una diferencia de velocidad? La respuesta es simplemente el volumen de código que se está utilizando. PL / SQL es un lenguaje interpretado por lo que cada línea extra de código se suma al tiempo total de procesamiento.

Uno puede entonces preguntar si la compilación nativa quitaría esta discrepancia. Esa pregunta se puede contestar muy fácilmente.

SQL> ALTER SESSION SET plsql_compiler_flags = 'NATIVE';
Session altered

SQL> ALTER PROCEDURE P_VERDADERA_COMPARACION_CURSOR COMPILE;
Procedure altered

SQL> ALTER SESSION SET plsql_compiler_flags = 'INTERPRETED';
Session altered

SQL> SET SERVEROUTPUT ON
SQL> EXEC P_VERDADERA_COMPARACION_CURSOR;

Explicit: 311
Implicit: 36

PL/SQL procedure successfully completed

SQL>

Esto demuestra que todavía hay una diferencia de velocidad entre los dos tipos de cursor, por lo que incluso cuando compilado nativamente la regla de «menos código es más rápido» sigue siendo válido.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.