sql >> Base de Datos >  >> RDS >> Oracle

¿Cómo procesa Oracle las llamadas a funciones almacenadas en SQL?

Es una muy buena pregunta.

Primero intenté crear una tabla e insertar datos de muestra (solo cinco filas):

create table my_table(value number);
insert into my_table(value) values(1);
insert into my_table(value) values(2);
insert into my_table(value) values(3);
insert into my_table(value) values(4);
insert into my_table(value) values(5);

Hice un paquete de prueba simple para probar esto.

create or replace package my_package is
  g_counter_SELECT PLS_INTEGER := 0; -- counter for SELECT statement
  g_counter_WHERE  PLS_INTEGER := 0; -- counter for WHERE clause
  function my_function(number_in in number, type_in in varchar2) return number;
  procedure reset_counter;
end;
/

Y cuerpo...

create or replace package body my_package is
  function my_function(number_in in number, type_in in varchar2) return number is
  begin
    IF(type_in = 'SELECT') THEN
        g_counter_SELECT := g_counter_SELECT + 1;
    ELSIF(type_in = 'WHERE') THEN
        g_counter_WHERE := g_counter_WHERE + 1;
    END IF;
    return mod(number_in, 2);
  end;
  procedure reset_counter is
  begin
    g_counter_SELECT := 0;
    g_counter_WHERE := 0;
  end;
end;
/

Ahora, podemos ejecutar la prueba en Oracle 9i (en 11g son los mismos resultados):

-- reset counter
exec my_package.reset_counter();

-- run query
select t.value, my_package.my_function(t.value, 'SELECT')
  from my_table t
 where my_package.my_function(t.value, 'WHERE') = 1;

-- print result
exec dbms_output.put_line('Count (SELECT) = ' || my_package.g_counter_SELECT);
exec dbms_output.put_line('Count (WHERE) = ' || my_package.g_counter_WHERE);

El resultado es:

DBMS Output (Session: [1] [email protected] at: 08.09.2010 01:50:04): 
-----------------------------------------------------------------------
Count (SELECT) = 3
Count (WHERE) = 5

Aquí está la tabla del plan:

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |       |       |       |
|*  1 |  TABLE ACCESS FULL   | MY_TABLE    |       |       |       |
--------------------------------------------------------------------

Lo que significa que la función (en WHERE calues) se llama para cada fila de la tabla (en el caso de FULL TABLE SCAN). En la instrucción SELECT se ejecuta tantas veces como se cumpla la condición WHERE my_function =1

Ahora... prueba tu segunda consulta (mismos resultados en Oracle9i y 11g)

El resultado es:

DBMS Output (Session: [1] [email protected] at: 08.09.2010 02:08:04): 
-----------------------------------------------------------------------
Count (SELECT) = 8
Count (WHERE) = 0

Explique el aspecto simple como este (para el modo de optimización ELEGIR):

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |       |       |       |
|*  1 |  TABLE ACCESS FULL   | MY_TABLE    |       |       |       |
--------------------------------------------------------------------

LA PREGUNTA ES:¿Por qué contar (SELECCIONAR) =8?

Debido a que Oracle primero ejecuta la subconsulta (en mi caso con FULL TABLE SCAN, son 5 filas =5 llamadas my_function en la instrucción SELECT):

select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t

Y que para esta vista (la subconsulta es como una vista) ejecute 3 veces (debido a la condición donde subquery.func_value =1) vuelva a llamar a la función my_function.

Personalmente, no recomiendo usar la función en la cláusula WHERE, pero admito que a veces esto es inevitable.

Como el peor ejemplo posible de esto se ilustra a continuación:

select t.value, my_package.my_function(t.value, 'SELECT')
  from my_table t
 where my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
   and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
   and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
   and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
   and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE');

Dónde está el resultado en Oracle 9i :

Count (SELECT) = 5
Count (WHERE) = 50

Y en Oracle 11g es :

Count (SELECT) = 5
Count (WHERE) = 5

Lo que en este caso demuestra que en ocasiones el uso de funciones puede ser crítico para el rendimiento. En otros casos (11g) lo resuelve la propia base de datos.