12c: optimisation des appels de fonctions PL/SQL à partir de SQL

Dans les requêtes SQL, on peut utiliser des fonctions standard (substr, to_date,… ) et lorsqu’on a un besoin un peu plus spécifique, on peut créer des des UDF (User Defined Function).
Avec Oracle, les fonctions standard sont codées en C, directement exécutable lors de l’exécution SQL. Mais les UDF sont simplement des appels à des fonctions PL/SQL stockées. Le problème lorsqu’on passe d’un language (SQL) à un autre (PL/SQL) en cours d’exécution: il y a un switch de contexte à chaque fois. C’est très consommateur de CPU. Surtout lorsqu’on appelle la fonction pour chaque ligne.

Jusqu’en 11g, les solutions sont:

  • Utiliser seulement des fonctions standard
  • Eviter l’appel ligne à ligne avec les pipelined functions
  • Lorsque le résultat de la fonction est déterministe et qu’il est applelé plusieurs fois pour les même valeurs, déclarer la fonction en deterministic

En 12c on a des vrais UDF optimisées pour les appels à partir de SQL.

On peut utiliser le pragma UDF pour une fonction stockée, par exemple:

create or replace function TEST_SPELL(n number) return varchar2 as pragma UDF; begin return to_char( date'-4712-01-01'+n-1,'Jsp'); end;
/
select max(spelled) from ( select TEST_SPELL(num) spelled from TEST);

Et on peut aussi déclarer la fonction comme un bloc PL/SQL anonyme dans la requête SQL:

with function TEST_SPELL(n number) return varchar2 as begin return to_char( date'-4712-01-01'+n-1,'Jsp'); end;
select /*+ TEST1 */ max(spelled) from ( select TEST_SPELL(num) spelled from TEST)
 /

Dans la demo j’appelle une fonction simple pour 1 million de lignes
Dans la version 11g, les temps ‘sql execute elapsed time’ et ‘PL/SQL execution elapsed time’ sont considérables, alors que ni la requête ni la fonction ne sont très complexes.
C’est parce chacun inclut le switch de contexte vers l’autre language.
En utilisant pragma udf ou le with plsql (déjà exposé ici) c’est une centaine de secondes que l’on gagne pour les 1 million d’appels.