Puis-je compter sur les fonctions exécutées en premier dans SQL

9

Veuillez considérer le script suivant:

create or replace function f(p_limit in integer) return integer as
begin
  set_global_context ('limit', p_limit);
  return p_limit;
end;
/

create view v as 
select level as val from dual connect by level<=sys_context('global_context','limit');

select f(2), v.* from v;

/*
F(2)                   VAL                    
---------------------- ---------------------- 
2                      1                      
2                      2                      
*/

select f(4), v.* from v;

/*
F(4)                   VAL                    
---------------------- ---------------------- 
4                      1                      
4                      2                      
4                      3                      
4                      4                      
*/

Puis-je compter sur l' f(x)exécution avant la lecture du contexte dans la vue, comme cela a été le cas dans ce scénario de test exécuté sur 10.2?

Jack dit d'essayer topanswers.xyz
la source
Je ne peux pas m'empêcher de penser qu'un déclencheur de connexion pourrait être plus approprié (si le niveau sera toujours le même, c'est-à-dire)
Philᵀᴹ
@Phil, ce n'est qu'un exemple - j'utilise sys_context pour paramétrer une vue et le paramètre sera différent à chaque fois. Si vous connaissez un moyen de définir un contexte global à partir de SQL sans déconner comme ça, je serais également intéressé d'entendre ça!
Jack dit d'essayer topanswers.xyz le
1
@JackDouglas: paramétrer une vue est une idée qui ne me semble pas "juste". Sous MSSQL, ce que vous essayez de faire pourrait être fait en utilisant une fonction définie par l'utilisateur qui renvoie un ensemble de résultats (plutôt qu'une valeur), - vous pouvez alors SELECT stuff FROM dbo.FuncReturningTable(param)ou similaire. Oracle a probablement des fonctionnalités équivalentes. Cependant, si j'utilise cela sur de grands ensembles de données, je ferais attention à surveiller les performances: je ne sais pas à quel point le planificateur de requêtes devrait être brillant pour faire un plan efficace à partir d'une telle syntaxe.
David Spillett
@David paramétrer une vue se fait généralement avec sys_context - normalement, vous devez définir le contexte avant d'exécuter la requête (par exemple avec un peu de PL / SQL). Oracle a des fonctions de retour et / ou de pipeline, mais ce n'est pas le moyen «normal» d'y parvenir. Pour être clair, je pense que la réponse à la question du titre est "non" - je me demandais simplement si quelqu'un savait mieux.
Jack dit d'essayer topanswers.xyz le

Réponses:

8

Non.

Si vous réécrivez votre vue avec le filtrage de contexte par rapport à la clause where (au lieu de la connexion par), vous obtiendrez la valeur précédemment définie pour le contexte:

create table t as 
 select rownum r from dual connect by level <= 10;

create or replace view v as 
  select r val from t where r <=sys_context('global_context','limit');

select f(2), v.* from v;

F(2) VAL
---- ---
   2   1 
   2   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 
   4   3 
   4   4 

La clause where étant évaluée avant la sélection des colonnes, la valeur transmise à la fonction n'est définie qu'après la lecture du contexte. L'emplacement de l'appel sys_context dans votre requête (sélectionner, où, grouper par, etc.) affectera exactement le moment où cette valeur est définie.

Chris Saxon
la source
+1 qui est à peu près "affaire close" dans mon livre, merci.
Jack dit d'essayer topanswers.xyz le
2

D'une manière générale, vous ne pouvez rien supposer de manière sûre de l'ordre dans lequel votre SGBD fera les choses lors de l'évaluation d'une seule instruction SQL. C'est pourquoi de nombreux SGBD n'autorisent pas les fonctions utilisées de cette façon à avoir des effets secondaires (c'est-à-dire que MSSQL ne permet pas aux fonctions de définir l'état global / de connexion, ce que vous faites là-bas, ou de modifier le contenu de la table). Une série d'instructions doit être exécutée d'une manière qui a du sens d'une étape à l'autre (c'est-à-dire qu'elles sont exécutées en série, ou de telle manière que vous ne pouvez pas dire qu'elles ne l'ont pas été), mais dans une seule instruction, le planificateur de requêtes a le règne libre tant qu'il n'introduit pas d'ambiguïté là où il n'existe pas déjà (dans votre exemple, l'ambiguïté existe déjà parce que la fonction a un effet secondaire affecte la vue).

Si le planificateur de requêtes était suffisamment lumineux pour détecter que la vue est affectée par les effets secondaires de la fonction, que ferait-il si vous rejoigniez une autre vue qui appelait potentiellement cette fonction avec différentes valeurs d'entrée? Cela pourrait très rapidement devenir très poilu - ce genre de chose est pourquoi généralement, dans n'importe quel contexte de programmation, les fonctions ne devraient pas avoir d'effets au-delà de leur propre sortie.

Dans cet exemple spécifique, je dirais qu'il est peu probable que f (x) soit appelé en premier, car c'est dans la partie "affichage" de l'instruction: le jeu de résultats de la vue est susceptible d'être récupéré avant toute fonction dans le la liste des colonnes à renvoyer est évaluée. Bien sûr, cela variera en fonction du SGBD utilisé: je ne suis pas un expert Oracle et vos résultats de test montrent que la fonction semble être appelée en premier dans ces cas. Mais je me méfierais de compter sur l'ordre d'exécution dans une seule instruction SQL tout de même - même si cela fonctionne toujours comme vous vous attendez en ce moment, il se peut qu'il ne le fasse pas dans les futures révisions (à moins qu'il ne soit officiellement documenté quelque part que l'exécution se déroulera toujours de cette façon).

David Spillett
la source
2
Bonne réponse, mais je pense que Jack cherche une réponse Oracle technique définitive.
Philᵀᴹ
1

La documentation promet seulement que "L'optimiseur évalue d'abord les expressions et les conditions contenant des constantes aussi complètement que possible." ( 10.2 , 11.2 ). Vous n'êtes pas assuré qu'il évaluera d'abord une expression particulière ou qu'il ne changera pas cet ordre de temps en temps (un nouveau niveau de patch dans la même version?).

Stephen Kendall
la source
+1 excellent, merci (bien qu'à ma lecture, ces documents ne correspondent pas tout à fait à la réponse de Chris )
Jack dit d'essayer topanswers.xyz
1
La différence est de savoir si la fonction est appelée dans la clause where, select ou une autre clause. Les fonctions de la section select n'affecteront pas les décisions de l'optimiseur (sauf s'il s'agit d'une sous-requête), elles n'ont donc pas besoin d'être évaluées jusqu'à la récupération des résultats. Les fonctions de la clause where affecteront la méthode de jointure utilisée, elles doivent donc être évaluées autant que possible dès que possible.
Chris Saxon
@Chris, est-ce cette expérience qui vous parle ou l'avez-vous obtenue quelque part des documents?
Jack dit d'essayer topanswers.xyz le
Je ne trouve pas de référence de document. D'après mon expérience, si elle est appelée dans la clause where pour filtrer une seule table, elle sera accessible pour chaque ligne (en supposant un FTS), mais uniquement pour les lignes renvoyées si dans la liste de sélection. Comme le plan d'exécution est défini pendant l'analyse, cela implique que les fonctions de select ne peuvent pas l'affecter. Un cas de test pour vérifier cela pourrait être fait en créant une fonction définissant un compteur (dans un package ou une table) et en comparant la sortie en fonction de l'endroit où elle est placée dans la requête.
Chris Saxon