2009-06-17 8 views
5

Diciamo che ho una chiamata di funzione su un selezionare o dove clausola in Oracle come questo:Oracle e la funzione SQLServer di valutazione nelle query

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

Un esempio simile può essere costruito per MS SQL Server.

Qual è il comportamento previsto in ciascun caso?

È la funzione HASH sta per essere chiamato una volta per ogni riga della tabella, o DBMS sarà abbastanza intelligente per chiamare la funzione solo una volta, dal momento che è una funzione con parametri costanti e senza effetti collaterali?

Grazie mille.

+0

Buona domanda per ragazzi DB. +1 – shahkalpesh

risposta

8

La risposta per Oracle dipende. La funzione verrà chiamata per ogni riga selezionata A MENO CHE la funzione sia contrassegnata come 'determinista', nel qual caso verrà chiamata una sola volta.

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; 

uscita:

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

3 rows selected 


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

1 rows selected 

Quindi la funzione StringLen() è stato chiamato tre volte nel primo caso. Ora, quando si esegue con StringLen2() che è indicato deterministica:

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; 

Risultati:

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

3 rows selected 

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

1 rows selected 

Quindi la funzione StringLen2() è stato chiamato solo una volta da quando è stata segnata deterministica.

Per una funzione non contrassegnato deterministica, è possibile aggirare il problema modificando la query in quanto tale:

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

Grazie per l'esempio, Mark. L'esempio ha SIDE-EFFECT sebbene (TotalFunctionCalls: = TotalFunctionCalls + 1;) quando si chiama la funzione StringLen. Quindi non penso che Oracle sarebbe in grado di ottimizzare in questo caso. –

+0

Impressionante Mark. Grande esempio. Quale versione Oracle hai usato per eseguirlo? –

+0

@Pablo Per quanto ne so, puoi contrassegnare qualsiasi funzione come deterministica ed è tua responsabilità assicurarti che non ci siano effetti collaterali. Se dbms_crypto.hash() non è deterministico, puoi sempre dichiarare la tua funzione marcata deterministica e chiamarla al suo posto. Ho eseguito questo su Oracle XE (che si basa su 10g). –

4

Per il server SQL, verrà valutato per ogni singola riga.

Sarà MOLTO meglio spegnere eseguendo la funzione una volta e assegnando a una variabile e utilizzando la variabile nella query.

+1

Mi hai battuto, ho fatto gli stessi punti ma hai digitato più velocemente. – HLGEM

1

risposta breve .... dipende.

Se la funzione accede ai dati, ORACLE non sa se sarà uguale per ogni riga, pertanto, deve eseguire una query per ciascuno. Se, ad esempio, la tua funzione è solo un formattatore che restituisce sempre lo stesso valore, puoi attivare la memorizzazione nella cache (contrassegnandola come Deterministica) che potrebbe consentire all'utente di eseguire la chiamata alla funzione solo una volta.

Qualcosa si potrebbe voler esaminare è Oracle con subquery:

La clausola WITH QUERY_NAME consente di assegnare un nome ad un blocco sottoquery. L'utente può quindi fare riferimento al blocco di subquery in più posizioni nella query tramite specificando il nome della query.Oracle ottimizza la query trattando il nome interrogazione sia come vista in linea o come una tabella temporanea

ho ricevuto il testo citato da here, che ha un sacco di esempi.

Problemi correlati