Oracle
 sql >> Base de données >  >> RDS >> Oracle

Problème avec les variables de liaison Oracle n'utilisant pas correctement l'index

C'est vraiment un sujet plus vaste, mais c'est l'approche qui, à mon avis, est la plus facile à mettre en œuvre et qui fonctionne bien. L'astuce consiste à utiliser SQL dynamique, mais implémentez-le de sorte que vous passiez toujours le même nombre de paramètres (nécessaires), ET que vous autorisiez Oracle à court-circuiter lorsque vous n'avez pas de valeur pour un paramètre (ce qui vous manque dans votre approche actuelle). Par exemple :

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;

Pour tester, lancez-le simplement. Par exemple :

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

Sur mon système, la table utilisée comporte plus de 100 mm de lignes avec un index sur le champ de numéro et le champ de nom. Revient presque instantanément. Notez également que vous ne voudrez peut-être pas faire une sélection * si vous n'avez pas besoin de toutes les colonnes, mais je suis un peu paresseux et j'utilise %rowtype pour cet exemple.

J'espère que ça aide