Can you use an index range scan with a LIKE if you have a leading wildcard in your expression?

e.g.

select *
from my_table
where my_column like '%something%'

Most people would say “no” but, I actually found a way to “trick” Oracle into doing so using bind variables, and taking advantage of bind-peeking.

create table drop_me2
(
    x primary key 
)
  compress 
as
select to_char(rownum)
from dual 
connect by level <= 1000000;

set serveroutput on

declare
  v_bind varchar2(20);
  v_dummy number;
begin
  
  v_bind := 'MOOSE%';

  -- Run the query once with a "properly" placed bind variable
  -- to trick Oracle when it bind-peeks
  select count(*) into v_dummy
  from drop_me2 a
  where a.x like v_bind;

  v_bind := '%MOOSE%';

  -- Then come back and run the exact same query, but change the bind
  -- variable.
  select count(*) into v_dummy
  from drop_me2 a
  where a.x like v_bind;
  
  --You'll see that both queries used the same plan.  The number of session logical reads on the second plan is significantly higher than the first.
  for v_row in (select *
                from table(dbms_xplan.display_cursor(format => '+predicate +peeked_binds')))
  loop
    dbms_output.put_line(v_row.plan_table_output);  
  end loop;
  
  execute immediate 'drop table drop_me2';
end;
/

Output:

SQL_ID  ftjrpvy6f3c13, child number 0
-------------------------------------
SELECT COUNT(*) FROM DROP_ME2 A WHERE A.X LIKE :B1
 
Plan hash value: 3147153515
 
---------------------------------------------------------------------------------
| Id  | Operation         | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |             |       |       |     3 (100)|          |
|   1 |  SORT AGGREGATE   |             |     1 |     7 |            |          |
|*  2 |   INDEX RANGE SCAN| SYS_C009054 |     1 |     7 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------------
 
Peeked Binds (identified by position):
--------------------------------------
 
   1 - :B1 (VARCHAR2(30), CSID=873): 'MOOSE%'
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("A"."X" LIKE :B1)
       filter("A"."X" LIKE :B1)

Free Oracle SQL Tuning Guide

Checkout my FREE guide, 7 SQL Tuning Secrets You Can Use Immediately, Even If You’ve Never Tuned a Query In Your Life!

Get it here: tuningsql.com/secrets

Leave a Reply

Your email address will not be published. Required fields are marked *