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

インデックスを適切に使用していないOracleバインド変数の問題

    これは本当に大きなトピックですが、これは実装が最も簡単でうまく機能すると思うアプローチです。秘訣は動的SQLを使用することですが、常に同じ数のパラメーター(必要)を渡すように実装し、パラメーターの値(不足しているもの)がない場合にOracleが短絡できるようにします。あなたの現在のアプローチ)。例:

    set serveroutput on
    create or replace procedure test_param(p1 in number default null, p2 in varchar2 default null) as
      l_sql varchar2(4000);
      l_cur sys_refcursor;
      l_rec my_table%rowtype;
      l_ctr number := 0;
    begin
    
      l_sql := 'select * from my_table where 1=1';
      if (p1 is not null) then
        l_sql := l_sql || ' and my_num_col = :p1';
      else
        -- short circuit for optimizer (1=1)
        l_sql := l_sql || ' and (1=1 or :p1 is null)';
      end if;
    
      if (p2 is not null) then
        l_sql := l_sql || ' and name like :p2';
      else
        -- short circuit for optimizer (1=1)
        l_sql := l_sql || ' and (1=1 or :p2 is null)';
      end if;
    
      -- show what the SQL query will be
      dbms_output.put_line(l_sql);
    
      -- note always have same param list (using)
      open l_cur for l_sql using p1,p2;
    
      -- could return this cursor (function), or simply print out first 10 rows here for testing
      loop
        l_ctr := l_ctr + 1;
        fetch l_cur
        into l_rec;
        exit when l_cur%notfound OR l_ctr > 10;
    
        dbms_output.put_line('Name is: ' || l_rec.name || ', Address is: ' || l_rec.address1);
      end loop;
      close l_cur;
    end;
    

    テストするには、それを実行するだけです。例:

    set serveroutput on
    -- using 0 param
    exec test_param();
    -- using 1 param
    exec test_param(123456789);
    -- using 2 params
    exec test_param(123456789, 'ABC%');
    

    私のシステムでは、使用されるテーブルは100mmを超える行で、数値フィールドと名前フィールドにインデックスが付いています。ほぼ瞬時に戻ります。また、すべての列が必要ない場合はselect *を実行したくない場合もありますが、私は少し怠惰で、この例では%rowtypeを使用しています。

    お役に立てば幸いです



    1. 日単位の2つのタイムスタンプ間のMySqlの違いは?

    2. SQLServerの再帰的自己結合

    3. SQLServerのインデックスの再構築と再編成

    4. MySQLのOUTPUT句