Dion Cho – Oracle Performance Storyteller

We are natural born scientists

Posts Tagged ‘sql profile

Optimizing Unoptimizeable SQL – dbms_advanced_rewrite

with one comment

One of the common requests very difficult to solve.

I have a bad performing query, but I don’t have access to source file. How can I change the execution plan?

Very bad request, but there are people who are in trouble as in this OTN form thread.

The good news is that some smart guys struggled to fight this and now we do have some well known techniques.

The last one – advanced query rewrite – is the topic today. This technique is especially useful under following situations.

  • Oracle 10g+
  • When stored outline and sql profile do not help – they use hints to control the execution plan, but there are cases when hints are useless.
  • Select, not DML
  • With no bind variables

Advanced query rewrite is designed as an assistance to mview query rewrite, but with above conditions met, we can enjoy it’s power with non-mview queries.

Following is a simple demonstration.

I have following query.

create table t1(c1, c2, c3, c4) ...;
create index t1_n1 on t1(c1);

select * from t1 where c1 like '%11%';



I have index on t1.c1, but Oracle can’t use it. What I like to do is convert the original query to this form.

select /*+ leading(x) use_nl(x t1) */ t1.*
from 
  (select /*+ index_ffs(t1) */ rowid as row_id, c1 
    from t1 where c1 like '%11%') x,
  t1
where
  t1.rowid = x.row_id
;



By preprocessing with index fast full scan, I’m trying to avoid the danger of full scan on very large table.

Look how I achieve it using advanced query rewrite.

UKJA@ukja116> create table t1(c1, c2, c3, c4)
UKJA@ukja116> as
UKJA@ukja116> select to_char(level), to_char(level), to_char(level), to_char(level)
UKJA@ukja116> from dual
UKJA@ukja116> connect by level  ;
UKJA@ukja116> 
UKJA@ukja116> create index t1_n1 on t1(c1);
UKJA@ukja116> 
UKJA@ukja116> -- This is current problematic query
UKJA@ukja116> explain plan for
  2  select *
  3  from t1
  4  where c1 like '%11%'; -- Look here! 

Explained.

UKJA@ukja116> 
UKJA@ukja116> select * from table(dbms_xplan.display);
--------------------------------------------------------------------------                                              
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |                                              
--------------------------------------------------------------------------                                              
|   0 | SELECT STATEMENT  |      | 50000 |  1318K|  1182   (3)| 00:00:06 |                                              
|*  1 |  TABLE ACCESS FULL| T1   | 50000 |  1318K|  1182   (3)| 00:00:06 |                                              
--------------------------------------------------------------------------                                              
                                                                                                                        
UKJA@ukja116> 
UKJA@ukja116> -- This is what I want
UKJA@ukja116> explain plan for
  2  select /*+ leading(x) use_nl(x t1) */ t1.*
  3  from
  4    (select /*+ index_ffs(t1) */ rowid as row_id, c1
  5  	 from t1 where c1 like '%11%') x,
  6    t1
  7  where
  8    t1.rowid = x.row_id
  9  ;

Explained.

UKJA@ukja116> select * from table(dbms_xplan.display);
-------------------------------------------------------------------------------------                                   
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |                                   
-------------------------------------------------------------------------------------                                   
|   0 | SELECT STATEMENT            |       | 50000 |  2246K| 50697   (1)| 00:04:14 |                                   
|   1 |  NESTED LOOPS               |       | 50000 |  2246K| 50697   (1)| 00:04:14 |                                   
|*  2 |   INDEX FAST FULL SCAN      | T1_N1 | 50000 |   927K|   653   (5)| 00:00:04 |                                   
|   3 |   TABLE ACCESS BY USER ROWID| T1    |     1 |    27 |     1   (0)| 00:00:01 |                                   
-------------------------------------------------------------------------------------                                   
                                                                                                                        
UKJA@ukja116> 
UKJA@ukja116> -- Advanced query rewrite is the answer
UKJA@ukja116> 
UKJA@ukja116> -- grant priv to ukja (as sys user)
UKJA@ukja116> -- grant execute on dbms_advanced_rewrite to ukja;
UKJA@ukja116> -- grant create materialized view to ukja;
UKJA@ukja116> 
UKJA@ukja116> alter session set query_rewrite_integrity = trusted;

Session altered.

UKJA@ukja116> 
UKJA@ukja116> 
UKJA@ukja116> begin
  2    sys.dbms_advanced_rewrite.declare_rewrite_equivalence (
  3  	  name		 => 'rewrite1',
  4  	  source_stmt =>
  5  'select *
  6  from t1
  7  where c1 like ''%11%''',
  8  	 destination_stmt =>
  9  'select /*+ leading(x) use_nl(x t1) */ t1.*
 10  from
 11    (select /*+ index_ffs(t1) */ rowid as row_id, c1
 12  	 from t1 where c1 like ''%11%'') x,
 13    t1
 14  where
 15    t1.rowid = x.row_id',
 16  	  validate	 => false,
 17  	  rewrite_mode	 => 'text_match');
 18  end;
 19  /

PL/SQL procedure successfully completed.

UKJA@ukja116> -- See how the execution plan is changed
UKJA@ukja116> explain plan for
  2  select *
  3  from t1
  4  where c1 like '%11%'
  5  ;

Explained.

UKJA@ukja116> select * from table(dbms_xplan.display);
-------------------------------------------------------------------------------------                                   
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |                                   
-------------------------------------------------------------------------------------                                   
|   0 | SELECT STATEMENT            |       | 50000 |  1660K| 50697   (1)| 00:04:14 |                                   
|   1 |  NESTED LOOPS               |       | 50000 |  1660K| 50697   (1)| 00:04:14 |                                   
|*  2 |   INDEX FAST FULL SCAN      | T1_N1 | 50000 |   341K|   653   (5)| 00:00:04 |                                   
|   3 |   TABLE ACCESS BY USER ROWID| T1    |     1 |    27 |     1   (0)| 00:00:01 |                                   
-------------------------------------------------------------------------------------                                   
                                                                                                                        
UKJA@ukja116> -- drop rewrite equivalence
UKJA@ukja116> begin
  2    sys.dbms_advanced_rewrite.drop_rewrite_equivalence( name=> 'rewrite1');
  3  end;
  4  /

PL/SQL procedure successfully completed.



Magical approach, isn’t it?

The stored outline and SQL profile are easy and common approaches, but this special case does not allow us to use them. The basic mechanism of both tricks is hints. I can’t make Oracle choose the index preprocessing(index fast full scan first, then table lookup by rowid) just by hints.

Consider to apply following technqiues before you beg of the arrogant developers to modify the source. :)

Written by Dion Cho

March 6, 2009 at 7:54 am

Follow

Get every new post delivered to your Inbox.

Join 60 other followers