I have a table with >500K rows. It has a few functional based indexes, using the UPPER() function on a column.
When I query the table, using the following criteria:
WHERE (UPPER(Col1) LIKE :bv5 OR UPPER(Col2) LIKE :bv5)
I get an FTS:
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 187 | 2840 (3)| 00:00:35 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 1 | 187 | 2840 (3)| 00:00:35 |
|* 3 | SORT GROUP BY STOPKEY| | 1 | 187 | 2840 (3)| 00:00:35 |
|* 4 | TABLE ACCESS FULL | Tab1 | 57118 | 10M| 2834 (3)| 00:00:35 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=100)
3 - filter(ROWNUM<=100)
4 - filter(UPPER("Col1") LIKE :BV5 OR UPPER("Col2") LIKE
:BV5)
Fair enough - CBO doesn't know how to evaluate the BV in order to use the index.
Hard coding the values instead of using the BV (as per other posts regarding bv's for transactions, direct values
for searches):
WHERE (UPPER(Col1) LIKE 'search value%' OR UPPER(Col2) LIKE 'search value%')
I get better response, presumbaly because the indexes are utilised:
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 187 | 21 (15)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 1 | 187 | 21 (15)| 00:00:01 |
|* 3 | SORT GROUP BY STOPKEY | | 1 | 187 | 21 (15)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID | Tab1 | 373 | 69751 | 20 (10)| 00:00:01 |
| 5 | BITMAP CONVERSION TO ROWIDS | | | | | |
| 6 | BITMAP OR | | | | | |
| 7 | BITMAP CONVERSION FROM ROWIDS| | | | | |
| 8 | SORT ORDER BY | | | | | |
|* 9 | INDEX RANGE SCAN | S2_FNC | | | 2 (0)| 00:00:01 |
| 10 | BITMAP CONVERSION FROM ROWIDS| | | | | |
| 11 | SORT ORDER BY | | | | | |
|* 12 | INDEX RANGE SCAN | S1_FNC | | | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=100)
3 - filter(ROWNUM<=100)
9 - access(UPPER("Col2") LIKE 'search value%')
filter(UPPER("Col2") LIKE 'search value%' AND UPPER("Col2")
LIKE 'search value%')
12 - access(UPPER("Col1") LIKE 'search value%')
filter(UPPER("Col1") LIKE 'search value%' AND UPPER("Col1")
LIKE 'search value%')
With that in mind, with both BV's and using the indexes, but using the INDEX_COMBINE hint:
SELECT /*+ INDEX_COMBINE(g) */
...
WHERE (UPPER(Col1) LIKE :bv5 OR UPPER(Col2) LIKE :bv5)
I found I can achieve the same plan:
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 187 | | 5560 (1)| 00:01:07 |
|* 1 | COUNT STOPKEY | | | | | | |
| 2 | VIEW | | 1 | 187 | | 5560 (1)| 00:01:07 |
|* 3 | SORT GROUP BY STOPKEY | | 1 | 187 | | 5560 (1)| 00:01:07 |
| 4 | TABLE ACCESS BY INDEX ROWID | Tab1 | 57118 | 10M| | 5554 (1)| 00:01:07 |
| 5 | BITMAP CONVERSION TO ROWIDS | | | | | | |
| 6 | BITMAP OR | | | | | | |
| 7 | BITMAP CONVERSION FROM ROWIDS| | | | | | |
| 8 | SORT ORDER BY | | | | 936K| | |
|* 9 | INDEX RANGE SCAN | S1_FNC | | | | 21 (0)| 00:00:01 |
| 10 | BITMAP CONVERSION FROM ROWIDS| | | | | | |
| 11 | SORT ORDER BY | | | | 936K| | |
|* 12 | INDEX RANGE SCAN | S2_FNC | | | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=100)
3 - filter(ROWNUM<=100)
9 - access(UPPER("Col1") LIKE :BV5)
filter(UPPER("Col1") LIKE :BV5 AND UPPER("Col1") LIKE :BV5)
12 - access(UPPER("Col2") LIKE :BV5)
filter(UPPER("Col2") LIKE :BV5 AND UPPER("Col2") LIKE :BV5)
However, here the Cost is up and the time taken is greater!
Also, note that the stats are exactly the same across all 3 queries:
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
143 consistent gets
0 physical reads
0 redo size
1185 bytes sent via SQL*Net to client
338 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
6 rows processed
Why am I not able to achieve the same performance with the bind variables + hint as with the hard coded values?
October 07, 2009 - 8:20 am UTC
you are not showing us everything here
... COUNT STOPKEY ....
you have obviously wrapped your query in a select count(*) with a rownum filter.
Use first_rows(N) as your hint and use bind variables. Set N to what you would be using with your rownum filter.
but, in a generic search like that, I'd probably be looking at union or union all - something like
ops$tkyte%ORA11GR2> explain plan for
2 select /*+ first_rows(100) */ *
3 from t
4 where (upper(col1) like :x)
5 union all
6 select /*+ first_rows(100) */ *
7 from t
8 where upper(col2) like :x
9 and rownum <= 100
10 /
Explained.
ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 96193762
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 200 | 10800 | 42 (50)| 00:00:01 |
| 1 | UNION-ALL | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID | T | 100 | 5400 | 21 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | T_IDX1 | | | 3 (0)| 00:00:01 |
|* 4 | COUNT STOPKEY | | | | | |
| 5 | TABLE ACCESS BY INDEX ROWID| T | 100 | 5400 | 21 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | T_IDX2 | | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access(UPPER("COL1") LIKE :X)
filter(UPPER("COL1") LIKE :X)
4 - filter(ROWNUM<=100)
6 - access(UPPER("COL2") LIKE :X)
filter(UPPER("COL2") LIKE :X)
22 rows selected.