Evaluación de funciones de Oracle y SQLServer en consultas
-
06-07-2019 - |
Pregunta
Digamos que tengo una llamada de función en una selección o donde cláusula en Oracle como esta:
select a, b, c, dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3)
from my_table
Se puede construir un ejemplo similar para MS SQLServer.
¿Cuál es el comportamiento esperado en cada caso?
¿La función HASH se llamará una vez para cada fila de la tabla, o DBMS será lo suficientemente inteligente como para llamar a la función solo una vez, ya que es una función con parámetros constantes y sin efectos secundarios ?
Muchas gracias.
Solución
La respuesta para Oracle es que depende. Se llamará a la función para cada fila seleccionada A MENOS que la función esté marcada como 'Determinista', 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
Entonces, la función StringLen () se llamó 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
Entonces, la función StringLen2 () solo se llamó una vez, ya que se marcó como determinista.
Para una función no marcada como determinista, puede evitar esto modificando 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
);
Otros consejos
Para el servidor SQL, se evaluará para cada fila individual.
Será mucho mejor ejecutando la función una vez y asignándola a una variable y utilizando la variable en la consulta.
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 una. Si, por ejemplo, su función es solo un formateador que siempre devuelve el mismo valor, puede activar el almacenamiento en caché (marcándolo como determinista), lo que puede permitirle hacer solo la llamada a la función una vez.
Algo que tal vez desee analizar es ORACLE WITH subquery:
La cláusula WITH query_name te permite asignar un nombre a un bloque de subconsulta. usted puede hacer referencia al bloque de subconsulta múltiples lugares en la consulta por especificando el nombre de la consulta. Oráculo optimiza la consulta tratando el nombre de la consulta como una vista en línea o como una tabla temporal
Obtuve el texto citado de aquí , que tiene muchos ejemplos.