sql >> データベース >  >> RDS >> Oracle

OracleはSQLでストアド関数呼び出しをどのように処理しますか?

    それは本当に良い質問です。

    最初にテーブルを作成してサンプルデータを挿入しようとしました(5行のみ):

    create table my_table(value number);
    insert into my_table(value) values(1);
    insert into my_table(value) values(2);
    insert into my_table(value) values(3);
    insert into my_table(value) values(4);
    insert into my_table(value) values(5);
    

    これをテストするための簡単なテストパッケージを作成しました。

    create or replace package my_package is
      g_counter_SELECT PLS_INTEGER := 0; -- counter for SELECT statement
      g_counter_WHERE  PLS_INTEGER := 0; -- counter for WHERE clause
      function my_function(number_in in number, type_in in varchar2) return number;
      procedure reset_counter;
    end;
    /
    

    そして体...

    create or replace package body my_package is
      function my_function(number_in in number, type_in in varchar2) return number is
      begin
        IF(type_in = 'SELECT') THEN
            g_counter_SELECT := g_counter_SELECT + 1;
        ELSIF(type_in = 'WHERE') THEN
            g_counter_WHERE := g_counter_WHERE + 1;
        END IF;
        return mod(number_in, 2);
      end;
      procedure reset_counter is
      begin
        g_counter_SELECT := 0;
        g_counter_WHERE := 0;
      end;
    end;
    /
    

    これで、Oracle 9iでテストを実行できます(11gでも同じ結果です):

    -- reset counter
    exec my_package.reset_counter();
    
    -- run query
    select t.value, my_package.my_function(t.value, 'SELECT')
      from my_table t
     where my_package.my_function(t.value, 'WHERE') = 1;
    
    -- print result
    exec dbms_output.put_line('Count (SELECT) = ' || my_package.g_counter_SELECT);
    exec dbms_output.put_line('Count (WHERE) = ' || my_package.g_counter_WHERE);
    

    結果は次のとおりです:

    DBMS Output (Session: [1] [email protected] at: 08.09.2010 01:50:04): 
    -----------------------------------------------------------------------
    Count (SELECT) = 3
    Count (WHERE) = 5
    

    計画表は次のとおりです:

    --------------------------------------------------------------------
    | Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
    --------------------------------------------------------------------
    |   0 | SELECT STATEMENT     |             |       |       |       |
    |*  1 |  TABLE ACCESS FULL   | MY_TABLE    |       |       |       |
    --------------------------------------------------------------------
    

    これは、関数(WHERE calues)がテーブルのすべての行に対して呼び出されることを意味します(FULL TABLE SCANの場合)。 SELECTステートメントでは、条件WHERE my_function =1

    に準拠する回数だけ起動されます。

    次に...2番目のクエリをテストします (Oracle9iと11gで同じ結果)

    結果は次のとおりです:

    DBMS Output (Session: [1] [email protected] at: 08.09.2010 02:08:04): 
    -----------------------------------------------------------------------
    Count (SELECT) = 8
    Count (WHERE) = 0
    

    このようにわかりやすい説明をしてください(CHOOSEオプティマイザーモードの場合):

    --------------------------------------------------------------------
    | Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
    --------------------------------------------------------------------
    |   0 | SELECT STATEMENT     |             |       |       |       |
    |*  1 |  TABLE ACCESS FULL   | MY_TABLE    |       |       |       |
    --------------------------------------------------------------------
    

    質問は次のとおりです。カウント(SELECT)=8を選択する理由

    Oracleは最初にサブクエリを実行するため(私の場合はFULL TABLE SCANの場合、5行=SELECTステートメントでmy_functionを5回呼び出します):

    select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t
    

    そして、このビュー(サブクエリはビューに似ています)よりも(subquery.func_value =1の条件のために)3回実行し、関数my_functionを再度呼び出します。

    個人的にはWHERE句で関数を使用することはお勧めしませんが、これが避けられない場合があることは認めます。

    これの最悪の例として、次のように説明されています。

    select t.value, my_package.my_function(t.value, 'SELECT')
      from my_table t
     where my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
       and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
       and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
       and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
       and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE');
    

    Oracle9iでの結果はどこにありますか

    Count (SELECT) = 5
    Count (WHERE) = 50
    

    Oracle11gでは

    Count (SELECT) = 5
    Count (WHERE) = 5
    

    この場合、これは、関数の使用がパフォーマンスにとって重要な場合があることを示しています。その他の場合(11g)は、データベース自体を解決します。



    1. ClusterControlCLIからPostgreSQLデータベースを管理する方法

    2. auto_explainの概要:遅いPostgresクエリプランを自動的にログに記録する方法

    3. MySQLコマンド:一般的なMySQLクエリのチートシート

    4. 複数の顧客が個別の残高を持っている場合に、クレジットとデビットからの実行残高を表示するMySqlクエリを作成する方法