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

Oracleでグローバル一時テーブルを回避する方法

    最初に2番目の質問に答えましょう:

    「なぜGTTから離れるのですか?本当にそんなに悪いのですか?」

    数日前、私は、大きなXMLファイル(〜18MB)をXMLTypeにロードする概念実証をノックアップしていました。 XMLTypeを永続的に格納したくなかったため、PL / SQL変数(セッション・メモリ)と一時表にロードしてみました。一時テーブルへのロードには、XMLType変数へのロードの5倍の時間がかかりました(1秒に対して5秒)。違いは、一時テーブルがメモリ構造ではないためです。一時テーブルはディスク(具体的には指定された一時テーブルスペース)に書き込まれます。

    大量のデータをキャッシュしたい場合、それをメモリに保存するとPGAにストレスがかかります。これは、セッションが多い場合には適切ではありません。つまり、RAMと時間の間のトレードオフです。

    最初の質問へ:

    「誰かが上記のサンプルクエリをコレクションやカーソルに変換する方法を示すことができますか?」

    投稿したクエリは、1つのステートメントにマージできます:

    SELECT case when a.column_a IS NULL OR a.column_a = ' ' 
               then b.data_a
               else  column_a end AS someA,
           a.someB,
           a.someC
    FROM TABLE_A a
          left outer join TABLE_B b
              on ( a.column_b = b.data_b AND a.column_c = 'C' )
    WHERE condition_1 = 'YN756'
      AND type_cd = 'P'
      AND TO_NUMBER(TO_CHAR(m_date, 'MM')) = '12'
      AND (lname LIKE (v_LnameUpper || '%') OR
      lname LIKE (v_searchLnameLower || '%'))
      AND (e_flag = 'Y' OR
      it_flag = 'Y' OR
      fit_flag = 'Y'));
    

    (私は単にあなたのロジックを置き換えましたが、そのcase() ステートメントは、より適切なnvl2(trim(a.column_a), a.column_a, b.data_a)に置き換えることができます。 。

    クエリはもっと​​複雑だとおっしゃっていますが、最初の呼び出しポートは、クエリの書き換えを検討することです。厄介なクエリをPL/SQLでつなぎ合わせた多数のベイビーSQLに分割することがどれほど魅力的かは知っていますが、純粋なSQLの方がはるかに効率的です。

    コレクションを使用するには、SQLでタイプを定義するのが最適です。これにより、PL/SQLだけでなくSQLステートメントでもタイプを柔軟に使用できるようになります。

    create or replace type tab_a_row as object
        (col_a number
         , col_b varchar2(23)
         , col_c date);
    /
    create or replace type tab_a_nt as table of tab_a_row;
    /
    

    結果セットを返すサンプル関数は次のとおりです。

    create or replace function get_table_a 
          (p_arg in number) 
          return sys_refcursor 
    is 
        tab_a_recs tab_a_nt; 
        rv sys_refcursor; 
    begin 
        select tab_a_row(col_a, col_b, col_c)  
        bulk collect into tab_a_recs 
        from table_a 
        where col_a = p_arg; 
    
        for i in tab_a_recs.first()..tab_a_recs.last() 
        loop 
            if tab_a_recs(i).col_b is null 
            then 
                tab_a_recs(i).col_b :=  'something'; 
            end if; 
        end loop;  
    
        open rv for select * from table(tab_a_recs); 
        return rv; 
    end; 
    / 
    

    そして、ここでそれが実行されています:

    SQL> select * from table_a
      2  /
    
         COL_A COL_B                   COL_C
    ---------- ----------------------- ---------
             1 whatever                13-JUN-10
             1                         12-JUN-10
    
    SQL> var rc refcursor
    SQL> exec :rc := get_table_a(1)
    
    PL/SQL procedure successfully completed.
    
    SQL> print rc
    
         COL_A COL_B                   COL_C
    ---------- ----------------------- ---------
             1 whatever                13-JUN-10
             1 something               12-JUN-10
    
    SQL>
    

    関数では、ORA-00947例外を回避するために、列を使用して型をインスタンス化する必要があります。 PL / SQL表タイプを設定する場合、これは必要ありません。

    SQL> create or replace procedure pop_table_a
      2        (p_arg in number)
      3  is
      4      type table_a_nt is table of table_a%rowtype;
      5      tab_a_recs table_a_nt;
      6  begin
      7      select *
      8      bulk collect into tab_a_recs
      9      from table_a
     10      where col_a = p_arg;
     11  end;
     12  /
    
    Procedure created.
    
    SQL> 
    

    最後に、ガイドライン

    「GTTをいつ使用するか、いつ回避するかについてのガイドラインはどうあるべきか」

    グローバル一時テーブルは、同じセッション内の異なるプログラムユニット間でキャッシュされたデータを共有する必要がある場合に非常に適しています。たとえば、いくつかの手順の1つによって入力されたGTTをフィードする単一の関数によって生成された一般的なレポート構造があるとします。 (それでも動的参照カーソルを使用して実装できますが...)

    グローバル一時テーブルは、単一のSQLクエリで解決するには複雑すぎる中間処理がたくさんある場合にも適しています。特に、その処理を取得した行のサブセットに適用する必要がある場合。

    ただし、一般的には、一時テーブルを使用する必要はないという前提があります。だから

    1. 難しい場合を除いて、SQLで実行してください...
    2. ...メモリを大量に消費しない限り、PL / SQL変数(通常はコレクション)で実行します...
    3. ...グローバル一時テーブルを使用して実行します


    1. PostgreSQLで挿入パフォーマンスを高速化する方法

    2. SQLServerの行番号で内部結合を行う方法

    3. Oracle-読み取り専用ユーザーを作成する方法

    4. SQLLIMITおよびOFFSETクエリを使用してすべてのレコードを選択する