2009-06-17 11 views
5

Lassen Sie uns sagen, dass ich einen Funktionsaufruf auf einem haben wählen oder wo Klausel in Oracle wie folgt aus:Oracle und SQL Server-Funktion Auswertung in Abfragen

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

Ein ähnliches Beispiel kann für MS SQL Server aufgebaut werden.

Was ist das erwartete Verhalten in jedem Fall?

Ist HASH Funktion für jede Zeile in der Tabelle einmal aufgerufen gehen werden, oder DBMS wird klug genug sein, nur ein einziges Mal die Funktion aufgerufen werden, da sie eine Funktion mit konstanten Parametern sind und keine Nebenwirkungen?

Vielen Dank.

+0

Gute Frage für DB Jungs. +1 – shahkalpesh

Antwort

8

Die Antwort für Oracle hängt davon ab. Die Funktion wird für jede ausgewählte Zeile aufgerufen, wenn die Funktion nicht 'Deterministisch' ist. In diesem Fall wird sie nur einmal aufgerufen.

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; 

Ausgang:

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

3 rows selected 


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

1 rows selected 

So ist die StringLen() Funktion dreimal im ersten Fall genannt wurde. Nun, wenn sie mit StringLen2() ausführt, die deterministisch bezeichnet wird:

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; 

Ergebnisse:

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

3 rows selected 

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

1 rows selected 

So wurde die StringLen2() Funktion nur einmal aufgerufen, da sie deterministisch geprägt war.

Für eine Funktion nicht deterministisch markiert, können Sie dieses Problem umgehen, indem Sie Ihre Abfrage als solche Modifikation:

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

Danke für das Beispiel, Mark. Ihr Beispiel hat jedoch einen SIDE-EFFECT (TotalFunctionCalls: = TotalFunctionCalls + 1;) beim Aufrufen der StringLen-Funktion. Ich denke also nicht, dass Oracle in diesem Fall optimieren könnte. –

+0

Ehrfürchtige Markierung. Tolles Beispiel. In welcher Oracle-Version hast du das ausgeführt? –

+0

@Pablo Soweit ich weiß, können Sie jede Funktion als deterministisch markieren und es liegt in Ihrer Verantwortung sicherzustellen, dass es keine Nebenwirkungen gibt. Wenn dbms_crypto.hash() nicht deterministisch ist, können Sie Ihre eigene Funktion als deterministisch deklarieren und diese stattdessen aufrufen. Ich habe dies auf Oracle XE (die auf 10g basiert) ausgeführt. –

4

Für SQL Server wird es für jede einzelne Zeile ausgewertet.

Sie sind viel besser dran, indem Sie die Funktion einmal ausführen und einer Variablen zuweisen und die Variable in der Abfrage verwenden.

+1

Sie haben mich geschlagen, ich hatte die gleichen Punkte gemacht, aber Sie tippten schneller. – HLGEM

1

kurze Antwort .... es hängt davon ab.

Wenn die Funktion auf Daten zugreift ORACLE weiß nicht, ob es das gleiche für jede Reihe sein wird, daher muss es für jeden abzufragen. Wenn Ihre Funktion beispielsweise nur ein Formatierer ist, der immer denselben Wert zurückgibt, können Sie das Caching aktivieren (als Deterministic markieren), wodurch Sie den Funktionsaufruf nur einmal ausführen können.

Etwas, das Sie in aussehen wollen kann, ist Oracle mit Unterabfrage:

Die WITH QUERY_NAME Klausel können Sie einen Namen zu einer Unterabfrage Block zuweisen . Sie können dann den Unterabfrageblock mehrere Stellen in der Abfrage von unter Angabe des Abfrage-Namen verweisen.Oracle optimiert die Abfrage durch die Abfragenamen entweder als ein Inline-View oder als temporäre Tabelle

Ich habe den zitierten Text von here, die Behandlung, die viele Beispiele hat.

Verwandte Themen