Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Problem mit Oracle-Bind-Variablen, die den Index nicht richtig verwenden

Dies ist wirklich ein größeres Thema, aber das ist der Ansatz, der meiner Meinung nach am einfachsten zu implementieren ist und gut funktioniert. Der Trick besteht darin, dynamisches SQL zu verwenden, es jedoch so zu implementieren, dass Sie immer die gleiche Anzahl von Parametern (erforderlich) übergeben, UND Sie zulassen, dass Oracle kurzschließt, wenn Sie keinen Wert für einen Parameter haben (was Ihnen fehlt). Ihre aktuelle Vorgehensweise). Zum Beispiel:

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;

Führen Sie es zum Testen einfach aus. Zum Beispiel:

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

Auf meinem System hat die verwendete Tabelle über 100 mm Zeilen mit einem Index auf dem Nummernfeld und dem Namensfeld. Geht fast sofort zurück. Beachten Sie auch, dass Sie möglicherweise kein select * ausführen möchten, wenn Sie nicht alle Spalten benötigen, aber ich bin ein bisschen faul und verwende für dieses Beispiel %rowtype.

Hoffe das hilft