2009-06-17 11 views
5

はのは、私がに関数呼び出しがあるとしましょう。このようなオラクルで句または選択:OracleとSQLServerの機能評価

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

同様の例は、MS SQLServerのために構築することができます。

いずれの場合でも予想される動作は何ですか?

は、それが一定のパラメータを持つ機能なし副作用なので、HASH関数は、テーブル内の各行に1回呼び出される予定、またはDBMSは一度だけ関数を呼び出すには十分にスマートになりますか?

ありがとうございます。

+0

DBの人には良い質問です。 +1 – shahkalpesh

答えて

8

オラクルの答えはそれに依存します。この関数は、関数が「決定的」とマークされている場合を除いて、選択された行ごとに呼び出されます。この場合、関数は一度だけ呼び出されます。

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 
); 
+0

Markさん、ありがとうございました。あなたの例は、StringLen関数を呼び出すときにSIDE-EFFECT though(TotalFunctionCalls:= TotalFunctionCalls + 1;)を持っています。だから私はオラクルがこの場合最適化できるとは思わない。 –

+0

恐ろしいマーク。素晴らしい例。あなたはそれを実行するためにどのOracleバージョンを使用しましたか? –

+0

@Pablo私が知っている限り、あなたは決定的なものとして任意の関数をマークすることができ、副作用がないことを確認するのはあなたの責任です。 dbms_crypto.hash()が決定的でない場合は、常にdeterministicとマークされた独自の関数を宣言して代わりに呼び出すことができます。 Oracle XE(これは10gに基づいています)でこれを実行しました。 –

4

SQLサーバーの場合、1行ごとに評価されます。

関数を一度実行し、変数に代入してクエリ内の変数を使用すると、より良い結果が得られます。

+1

あなたは私を打つ、私は同じ点を作ったが、より速く入力した。 – HLGEM

1

短い答え....それが依存しています。

関数がデータにアクセスしている場合、ORACLEは行ごとに同じであるかどうかわからないため、それぞれを照会する必要があります。たとえば、関数が常に同じ値を返す単なるフォーマッタの場合、キャッシングをオンにすることができます(確定的としてマークする)。これにより、関数呼び出しを1回だけ行うことができます。あなたに見てみたいことがあります

何かがサブクエリWITH ORACLEです:

ザ・WITH QUERY_NAME句を使用すると、副問合せブロックに名前を割り当てることができます 。 は、サブクエリブロック をクエリ内の複数の場所を参照するために、 によってクエリ名を指定できます。オラクル は、私はたくさんの例がありhereから引用されたテキストを、持っ

一時テーブルとしてインライン・ビューまたは のいずれかとし クエリ名を処理することにより、クエリを最適化します。

関連する問題