2009-06-17 24 views
5

Digamos que tengo una llamada de función en una seleccione o donde cláusula en Oracle como esto:Oracle y la función SQL Server evaluación en las consultas

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

Un ejemplo similar se puede construir para MS SQL Server.

¿Cuál es el comportamiento esperado en cada caso?

es la función HASH va a ser llamado una vez para cada fila de la tabla, o DBMS será lo suficientemente inteligente como para llamar a la función una sola vez, ya que es una función con parámetros constantes y no hay efectos secundarios?

Muchas gracias.

+0

Buena pregunta para DB chicos. +1 – shahkalpesh

Respuesta

8

La respuesta para Oracle depende. Se llamará a la función para cada fila seleccionada A MENOS que la Función esté marcada como "Determinística", en cuyo caso solo se llamará una vez.

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; 

Salida:

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

3 rows selected 


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

1 rows selected 

Por lo tanto la función StringLen() fue llamado tres veces en el primer caso. Ahora cuando se ejecuta con StringLen2() que se denota determinista:

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; 

Resultados:

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

3 rows selected 

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

1 rows selected 

Por lo tanto la función StringLen2() sólo se llama una vez desde que fue marcado determinista.

Para una función no determinista marcado, se puede evitar esto mediante la modificación de su consulta como tal:

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

Gracias por el ejemplo, Mark. Sin embargo, su ejemplo tiene un SIDE-EFFECT (TotalFunctionCalls: = TotalFunctionCalls + 1;) al llamar a la función StringLen. Entonces, no creo que Oracle pueda optimizar en este caso. –

+0

Awesome Mark. Buen ejemplo ¿Qué versión de Oracle usaste para ejecutarlo? –

+0

@Pablo Hasta donde yo sé, puede marcar cualquier función como determinista y es su responsabilidad asegurarse de que no haya ningún efecto secundario. Si dbms_crypto.hash() no es determinista, siempre puede declarar su propia función como determinista y llamarla en su lugar. Ejecuté esto en Oracle XE (que está basado en 10g). –

4

Para el servidor SQL, se evaluará para cada fila.

Estará mucho mejor ejecutando la función una vez y asignando una variable y usando la variable en la consulta.

+1

Me ganaste, tuve los mismos puntos pero escribiste más rápido. – HLGEM

1

respuesta corta .... depende.

Si la función está accediendo a los datos ORACLE no sabe si va a ser el mismo para cada fila, por lo tanto, necesita consultar cada uno. Si, por ejemplo, su función es solo un formateador que siempre devuelve el mismo valor, entonces puede activar el almacenamiento en caché (marcándolo como Determinista), lo que puede permitirle hacer solo la llamada de función una vez.

Algo es posible que desee ver en la sub consulta es ORACLE CON:

La cláusula WITH nombre_consulta le permite asignar un nombre a un bloque subconsulta. Usted puede hacer referencia al bloque de la subconsulta en varios lugares en la consulta por especificando el nombre de la consulta.Oracle optimiza la consulta, tratando el nombre consulta, ya sea como una vista en línea o como una tabla temporal

llegué el texto citado de here, que tiene un montón de ejemplos.

Cuestiones relacionadas