Oracle e SQLServer avaliação da função em consultas
-
06-07-2019 - |
Pergunta
Digamos que eu tenho uma chamada de função em um selecione ou onde cláusula no Oracle como esta:
select a, b, c, dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3)
from my_table
Um exemplo semelhante pode ser construído para o MS SQL Server.
O que é o comportamento esperado em cada caso?
O HASH função vai ser chamado uma vez para cada linha na tabela, ou DBMS será suficiente inteligente para chamar a função apenas uma vez, já que é uma função com parâmetros constantes e nenhum efeitos colaterais ?
Muito obrigado.
Solução
A resposta para Oracle é que depende. A função será chamada para cada linha selecionada menos que a função é marcada 'determinística' caso em que ela só vai ser chamado uma 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;
Output:
A TESTCALLCOUNT.STRINGLEN('FOO')
---------------------- ------------------------------
0 3
1 3
2 3
3 rows selected
TOTALFUNCTIONCALLS
----------------------
3
1 rows selected
Assim, a função StringLen () foi chamado três vezes no primeiro caso. Agora, quando executar com StringLen2 (), que é denotada 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;
Resultado:
A TESTCALLCOUNT.STRINGLEN2('FOO')
---------------------- -------------------------------
0 3
1 3
2 3
3 rows selected
TOTALFUNCTIONCALLS
----------------------
1
1 rows selected
Assim, a função StringLen2 () só foi chamado uma vez desde que foi marcada determinista.
Para uma função não marcado determinista, você pode contornar este problema, modificando sua 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
);
Outras dicas
Para o servidor SQL, ele será avaliado para cada linha.
Você vai ser muito melhor, executando a função uma vez e atribuir a uma variável e usar a variável na consulta.
resposta curta .... ele depende.
Se a função está acessando dados Oracle não sabe se ele vai ser o mesmo para cada linha, portanto, ele precisa de consulta para cada um. Se, por exemplo, a sua função é apenas um formatador que sempre retorna o mesmo valor, então você pode ativar o cache (marcando-o como determinística) que pode permitir que você só para fazer a chamada de função uma vez.
Algo que você pode querer olhar é ORACLE COM subconsulta:
A cláusula query_name COM permite atribuir um nome a um bloco de subconsulta. Vocês pode, em seguida, referência o bloco subconsulta vários lugares na consulta por especificando o nome da consulta. Oráculo otimiza a consulta tratando o nome da consulta ou como uma visão em linha ou como uma tabela temporária
Eu tenho o texto citado de aqui , que tem abundância de exemplos.