Domanda

Diciamo che ho una chiamata di funzione su una seleziona o dove in Oracle in questo modo:

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

Un esempio simile può essere creato per MS SQLServer.

Qual è il comportamento previsto in ciascun caso?

La funzione HASH verrà chiamata una volta per ogni riga della tabella, oppure DBMS sarà abbastanza intelligente da chiamare la funzione solo una volta, poiché è una funzione con parametri costanti e non effetti collaterali

Grazie mille.

È stato utile?

Soluzione

La risposta per Oracle è che dipende. La funzione verrà chiamata per ogni riga selezionata A MENO CHE la Funzione non sia contrassegnata come "Deterministica", 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;

Output:

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

3 rows selected


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

1 rows selected

Quindi la funzione StringLen () è stata chiamata tre volte nel primo caso. Ora durante l'esecuzione con StringLen2 () che è indicato deterministico:

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 () è stata chiamata una sola volta poiché è stata contrassegnata come deterministica.

Per una funzione non contrassegnata come deterministica, puoi aggirare il problema modificando la tua 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
);

Altri suggerimenti

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

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

risposta breve .... dipende.

Se la funzione sta accedendo ai dati ORACLE non sa se sarà la stessa per ogni riga, pertanto deve eseguire una query per ciascuna. 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 di eseguire la chiamata di funzione una sola volta.

Qualcosa che potresti voler esaminare è ORACLE WITH subquery:

  

La clausola WITH query_name ti consente   assegnare un nome a un blocco di subquery. tu   può quindi fare riferimento al blocco di subquery   più posizioni nella query di   specificando il nome della query. Oracolo   ottimizza la query trattando il   nome della query come vista incorporata o   come tabella temporanea

Ho ricevuto il testo citato da qui , che contiene numerosi esempi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top