クエリでのOracleおよびSQLServer関数の評価
-
06-07-2019 - |
質問
次のように、Oracleの select または where 句で関数呼び出しがあるとします:
select a, b, c, dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3)
from my_table
同様の例をMS SQLServerに構築できます。
それぞれの場合に予想される動作は何ですか?
HASH 関数はテーブル内の各行に対して1回呼び出されますか、それとも定数パラメーターを持ち、副作用?
どうもありがとう。
解決
Oracleの答えは、依存することです。この関数は、「決定的」とマークされていない限り、選択されたすべての行に対して呼び出されます。その場合、関数は一度だけ呼び出されます。
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;
出力:
A TESTCALLCOUNT.STRINGLEN('FOO')
---------------------- ------------------------------
0 3
1 3
2 3
3 rows selected
TOTALFUNCTIONCALLS
----------------------
3
1 rows selected
したがって、最初のケースではStringLen()関数が3回呼び出されました。決定論的に示されるStringLen2()で実行する場合:
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;
結果:
A TESTCALLCOUNT.STRINGLEN2('FOO')
---------------------- -------------------------------
0 3
1 3
2 3
3 rows selected
TOTALFUNCTIONCALLS
----------------------
1
1 rows selected
したがって、StringLen2()関数は、確定的とマークされているため、一度だけ呼び出されました。
決定的とマークされていない関数については、クエリを次のように変更することでこれを回避できます。
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
);
他のヒント
SQLサーバーの場合、1行ごとに評価されます。
関数を1回実行し、変数に割り当てて、クエリで変数を使用することで、大幅に改善されます。
短い答え....それは依存します。
関数がデータにアクセスしている場合、ORACLEは各行で同じになるかどうかを知らないため、それぞれに対してクエリを実行する必要があります。たとえば、関数が常に同じ値を返す単なるフォーマッターである場合は、キャッシュを有効にして(確定的としてマークする)、関数呼び出しを1回だけ行うことができます。
調査したいのは、ORACLE WITHサブクエリです:
WITH query_name句を使用すると、 サブクエリブロックに名前を割り当てます。君は その後、サブクエリブロックを参照できます クエリ内の複数の場所 クエリ名を指定します。オラクル を処理してクエリを最適化します インラインビューとしてのクエリ名または 一時テーブルとして
こちらから引用テキストを入手しました。これには多くの例があります。