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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
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.
1 2 3 4 5 6 7 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
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; |
1 2 3 4 5 6 7 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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.