Question

Disons que j'ai un appel de fonction sur une clause select ou Where dans Oracle, comme ceci:

select a, b, c, dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3)
  from my_table

Un exemple similaire peut être construit pour MS SQLServer.

Quel est le comportement attendu dans chaque cas?

La fonction HASH sera-t-elle appelée une fois pour chaque ligne de la table ou le système de gestion de base de données sera-t-il assez intelligent pour appeler la fonction une seule fois, puisqu'il s'agit d'une fonction à paramètres constants et non effets secondaires ?

Merci beaucoup.

Était-ce utile?

La solution

La réponse pour Oracle est que cela dépend. La fonction sera appelée pour chaque ligne sélectionnée SAUF SI la fonction est marquée "Déterministe", auquel cas elle ne sera appelée qu'une fois.

CREATE OR REPLACE PACKAGE TestCallCount AS
    FUNCTION StringLen(SrcStr VARCHAR) RETURN INTEGER;
    FUNCTION StringLen2(SrcStr VARCHAR) RETURN INTEGER DETERMINISTIC;
    FUNCTION GetCallCount RETURN INTEGER;
    FUNCTION GetCallCount2 RETURN INTEGER;
END TestCallCount;

CREATE OR REPLACE PACKAGE BODY TestCallCount AS
    TotalFunctionCalls INTEGER := 0;
    TotalFunctionCalls2 INTEGER := 0;

    FUNCTION StringLen(SrcStr VARCHAR) RETURN INTEGER AS
    BEGIN
        TotalFunctionCalls := TotalFunctionCalls + 1;
        RETURN Length(SrcStr);
    END;
    FUNCTION GetCallCount RETURN INTEGER AS
    BEGIN
        RETURN TotalFunctionCalls;
    END;

    FUNCTION StringLen2(SrcStr VARCHAR) RETURN INTEGER DETERMINISTIC AS
    BEGIN
        TotalFunctionCalls2 := TotalFunctionCalls2 + 1;
        RETURN Length(SrcStr);
    END;
    FUNCTION GetCallCount2 RETURN INTEGER AS
    BEGIN
        RETURN TotalFunctionCalls2;
    END;

END TestCallCount;




SELECT a,TestCallCount.StringLen('foo') FROM(
    SELECT 0 as a FROM dual
    UNION
    SELECT 1 as a FROM dual
    UNION
    SELECT 2 as a FROM dual
);

SELECT TestCallCount.GetCallCount() AS TotalFunctionCalls FROM dual;

Sortie:

A                      TESTCALLCOUNT.STRINGLEN('FOO') 
---------------------- ------------------------------ 
0                      3                              
1                      3                              
2                      3                              

3 rows selected


TOTALFUNCTIONCALLS     
---------------------- 
3                      

1 rows selected

La fonction StringLen () a donc été appelée trois fois dans le premier cas. Maintenant, lors de l'exécution avec StringLen2 (), ce qui est noté déterministe:

SELECT a,TestCallCount.StringLen2('foo') from(
    select 0 as a from dual
    union
    select 1 as a from dual
    union
    select 2 as a from dual
);

SELECT TestCallCount.GetCallCount2() AS TotalFunctionCalls FROM dual;

Résultats:

A                      TESTCALLCOUNT.STRINGLEN2('FOO') 
---------------------- ------------------------------- 
0                      3                               
1                      3                               
2                      3                               

3 rows selected

TOTALFUNCTIONCALLS     
---------------------- 
1                      

1 rows selected

La fonction StringLen2 () n’a donc été appelée qu’une fois car elle a été marquée déterministe.

Pour une fonction non marquée déterministe, vous pouvez contourner ce problème en modifiant votre requête en tant que telle:

select a, b, c, hashed
  from my_table
cross join (
  select dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3) as hashed from dual
);

Autres conseils

Pour SQL Server, il sera évalué pour chaque ligne.

Vous gagnerez BEAUCOUP mieux en exécutant la fonction une seule fois, en attribuant une variable et en utilisant la variable dans la requête.

réponse courte .... ça dépend.

Si la fonction accède aux données, ORACLE ne sait pas si elles seront identiques pour chaque ligne. Par conséquent, elle doit interroger chacune d'elles. Si, par exemple, votre fonction est juste un formateur qui renvoie toujours la même valeur, vous pouvez activer la mise en cache (en la définissant comme déterministe), ce qui peut vous permettre de n’appeler la fonction qu’une seule fois.

Vous voudrez peut-être examiner ORACLE WITH, sous-requête:

  

La clause WITH nom_requête vous permet   attribuer un nom à un bloc de sous-requête. Vous   peut alors référencer le bloc de sous-requête   plusieurs endroits dans la requête par   en spécifiant le nom de la requête. Oracle   optimise la requête en traitant le   nom de la requête sous forme de vue en ligne ou   en tant que table temporaire

J'ai reçu le texte cité de ici , qui contient de nombreux exemples.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top