sql >> Base de Datos >  >> RDS >> Oracle

Problema con las variables de vinculación de Oracle que no usan el índice correctamente

Este es un tema más amplio en realidad, pero este es el enfoque que creo que es más fácil de implementar y funciona bien. El truco es usar SQL dinámico, pero implementarlo de modo que siempre pase la misma cantidad de parámetros (necesarios), Y permita que Oracle haga un cortocircuito cuando no tiene un valor para un parámetro (lo que le falta en su enfoque actual). Por ejemplo:

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;

Para probar, simplemente ejecútelo. Por ejemplo:

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

En mi sistema, la tabla utilizada tiene más de 100 mm de filas con un índice en el campo de número y el campo de nombre. Vuelve casi al instante. También tenga en cuenta que es posible que no desee hacer una selección * si no necesita todas las columnas, pero estoy siendo un poco perezoso y uso %rowtype para este ejemplo.

Espero que ayude