Domanda

I have a common problem with ORACLE in following example code:

create or replace procedure usp_test
(
  p_customerId number,
  p_eventTypeId number,
  p_out OUT SYS_REFCURSOR
)
as
begin

  open p_out for
    select e.Id from eventstable e
    where 
      (p_customerId is null or e.CustomerId = p_customerId)
      and
      (p_eventTypeId is null or e.EventTypeId = p_eventTypeId)
    order by Id asc;

end usp_test;

The "OR" in "(p_customerId is null or e.CustomerId = p_customerId)" kills procedure performance, because optimizer will not use index (i hope for index seek) on "CustomerId" column optimally, resulting in scan instead of seek. Index on "CustomerId" has plenty of distinct values.

When working with MSSQL 2008 R2 (latest SP) or MSSQL 2012 i can hint the query with "option(recompile)" which will:

  1. Recompile just this query
  2. Resolve values for all variables (they are known after sproc is called)
  3. Replace all resolved variables with constants and eliminate constant predicate parts

For example: if i pass p_customerId = 1000, then "1000 is null" expression will always be false, so optimizer will ignore it. This will add some CPU overhead, but it is used mostly for rarely called massive reports procedures, so no problems here.

Is there any way to do that in Oracle? Dynamic-SQL is not an option.

Adds

Same procedure just without "p_customerId is null" and "p_eventTypeId is null" runs for ~0.041 seconds, while the upper one runs for ~0.448 seconds (i have ~5.000.000 rows).

È stato utile?

Soluzione

CREATE INDEX IX_YOURNAME1 ON eventstable (NVL(p_customerId, 'x'));
CREATE INDEX IX_YOURNAME2 ON eventstable (NVL(p_eventTypeId, 'x'));

create or replace procedure usp_test
(
  p_customerId number,
  p_eventTypeId number,
  p_out OUT SYS_REFCURSOR
)
as
begin

  open p_out for
    select e.Id from eventstable e
    where 
        (NVL(p_customerId, 'x') = e.CustomerId OR NVL(p_customerId, 'x') = 'x')
    AND (NVL(p_eventTypeId, 'x') = e.EventTypeId OR NVL(p_eventTypeId, 'x') = 'x')
    order by Id asc;
end usp_test;

Altri suggerimenti

One column index can't help as it's not stored in index definition. Is creating index on (customer id, event id, id ) allowed? This way all needed columns are in index...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top