简体   繁体   中英

Oracle SQL index ignored when using OR

I've got a table with 2 columns and around 8 million rows. equip_no and op_id. These columns are indexed both individually and together in a single index. The values for both are all nearly unique.

I've found when performing a query which uses an AND on the 2 columns the indexes are used fine and performance is great, 8 million rows with sub second response times.

select *
from asset_table
where equip_no like 'something%'
AND op_id like 'somethingElse%'

However, if I take exactly the same query and change the AND to OR I suddenly end up with a TABLE ACCESS FULL in my plan and the query takes 20 or 30 seconds.

select *
from asset_table
where equip_no like 'something%'
OR op_id like 'somethingElse%'    

My Oracle version is as follows in case that's helpful.

Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bi

So I'm not entirely new to indexes but I've never really specialised in them. I was wondering if this was known behaviour or am I not doing something I should. I created the indexes with the following commands...

create index IDX_asset_TABLE_EQ_OPID
on asset_table(equip_no, op_id)

create index IDX_asset_TABLE_EQ
on asset_table(equip_no)

create index IDX_asset_TABLE_OPID
on asset_table(op_id)

Any help is appreciated. I've googled for quite some time and have had no luck so far.

Thanks

Check your table, column, and index stats and the information in the explain plan. Without that information it is hard to give a good answer. But I can explain the basics.

The 'and' makes your query more selective (less rows returned) which is what indexes are good for.

The 'or' makes your query less selective (returns more rows) which makes the full table scan look cheaper.

example:

 born in june and female  (1/12 * 1/2) = 1/24 of the rows 
 born in june or  female  (1/12 + 1/2) = 7/12 of the rows

I think oracle should use mechanism called "or expansion". But probably you don't have stats on table and index. You can rewrite your query to 3 union all or apply hint ' /*+ use_concat */' . The hint forces optimizer to use union all.

select  /*+ use_concat */ *
from asset_table
where equip_no like 'something%'
OR op_id like 'somethingElse%'    

Thanks for the responses above. While not complete solutions they provided information that pushed me in the right direction.

Ultimately the solution involved understanding why - the OR looked expensive - and providing a hint. I tried the suggested hint /*+ use_concat / and it helped a bit. Measurable but not great. In the end after borrowing a friends optimizer the following hint provided the best overall performance. / + INDEX(ASSET_TABLE) */

As you may have guessed the snippet I provided was part of a larger query. The snippet was the key performance limiting factor though, providing the selectivity on the end result with the rest just filling in fields.

Thanks for the responses.

Darren

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM