Oracle
 sql >> Base de données >  >> RDS >> Oracle

Comment Oracle traite-t-il les appels de fonctions stockées dans SQL ?

C'est une très bonne question.

J'ai d'abord essayé de créer une table et d'insérer des exemples de données (cinq lignes uniquement) :

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

J'ai créé un package de test simple pour tester cela.

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

Et le corps...

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

Maintenant, nous pouvons exécuter le test sur Oracle 9i (sur 11g, les mêmes résultats) :

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

Le résultat est :

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

Voici le tableau des plans :

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

Ce qui signifie que la fonction (dans les valeurs WHERE) est appelée pour chaque ligne de la table (dans le cas de FULL TABLE SCAN). Dans l'instruction SELECT est lancée autant de fois respectez la condition WHERE my_function =1

Maintenant... testez votre deuxième requête (mêmes résultats sur Oracle9i et 11g)

Le résultat est :

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

Expliquer l'apparence simple comme ceci (pour CHOISIR le mode optimiseur):

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

LA QUESTION EST :Pourquoi compter (SELECT) = 8 ?

Parce qu'Oracle exécute d'abord la sous-requête (dans mon cas avec FULL TABLE SCAN, c'est 5 lignes =5 appelle my_function dans l'instruction SELECT):

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

Et que pour cette vue (la sous-requête est comme la vue), exécutez 3 fois (en raison de la condition où subquery.func_value =1) appelez à nouveau la fonction my_function.

Personnellement, je ne recommande pas d'utiliser la fonction dans la clause WHERE, mais j'avoue que parfois cela est inévitable.

Comme le pire exemple possible de ceci est illustré par ce qui suit :

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');

Où se trouve le résultat sur Oracle 9i :

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

Et sur Oracle 11g est :

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

Ce qui dans ce cas montre que parfois l'utilisation de fonctions peut être critique pour les performances. Dans d'autres cas (11g), il résout la base de données elle-même.