Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Index not used due to type conversion?

I have a process that is performing badly due to full table scans on a particular table. I have computed statistics, rebuilt existing indices and tried adding new indices for this table but this hasn't solved the issue.

Can an implicit type conversion stop an index being used? What about other reasons? The cost of a full table scan is around 1000 greater than the index lookup should be.

EDIT:

SQL statement:

select unique_key 
from src_table 
where natural_key1 = :1 
and natural_key2 = :2 
and natural_key3 = :3;
  • Cardinality of natural_key1 is high, but there is a type conversion.
  • The other parts of the natural key are low cardinality, and bitmap indices are not enabled.
  • Table size is around 1,000,000 records.

Java code (not easily modifiable):

ps.setLong(1, oid);

This conflicts with the column datatype: varchar2

like image 240
parkr Avatar asked Nov 28 '22 12:11

parkr


2 Answers

an implicit conversion can prevent an index from being used by the optimizer. Consider:

SQL> CREATE TABLE a (ID VARCHAR2(10) PRIMARY KEY);
 
Table created
 
SQL> insert into a select rownum from dual connect by rownum <= 1e6;
 
1000000 rows inserted

This is a simple table but the datatype is not 'right', i-e if you query it like this it will full scan:

SQL> select * from a where id = 100;
 
ID
----------
100

This query is in fact equivalent to:

select * from a where to_number(id) = 100;

It cannot use the index since we indexed id and not to_number(id). If we want to use the index we will have to be explicit:

select * from a where id = '100';

In reply to pakr's comment: There are lots of rules concerning implicit conversions. One good place to start is the documentation. Among other things, we learn that:

During SELECT FROM operations, Oracle converts the data from the column to the type of the target variable.

It means that when implicit conversion occurs during a "WHERE column=variable" clause, Oracle will convert the datatype of the column and NOT of the variable, therefore preventing an index from being used. This is why you should always use the right kind of datatypes or explicitly converting the variable.

From the Oracle doc:

Oracle recommends that you specify explicit conversions, rather than rely on implicit or automatic conversions, for these reasons:

  • SQL statements are easier to understand when you use explicit datatype conversion functions.
  • Implicit datatype conversion can have a negative impact on performance, especially if the datatype of a column value is converted to that of a constant rather than the other way around.
  • Implicit conversion depends on the context in which it occurs and may not work the same way in every case. For example, implicit conversion from a datetime value to a VARCHAR2 value may return an unexpected year depending on the value of the NLS_DATE_FORMAT parameter.
  • Algorithms for implicit conversion are subject to change across software releases and among Oracle products. Behavior of explicit conversions is more predictable.
like image 81
Vincent Malgrat Avatar answered Dec 06 '22 18:12

Vincent Malgrat


Make you condition sargable, that is compare the field itself to a constant condition.

This is bad:

SELECT  *
FROM    mytable
WHERE   TRUNC(date) = TO_DATE('2009.07.21')

, since it cannot use the index. Oracle cannot reverse the TRUNC() function to get the range bounds.

This is good:

SELECT  *
FROM    mytable
WHERE   date >= TO_DATE('2009.07.21')
        AND date < TO_DATE('2009.07.22')

To get rid of implicit conversion, well, use explicit conversion:

This is bad:

SELECT  *
FROM    mytable
WHERE   guid = '794AB5396AE5473DA75A9BF8C4AA1F74'

-- This uses implicit conversion. In fact this is RAWTOHEX(guid) = '794AB5396AE5473DA75A9BF8C4AA1F74'

This is good:

SELECT  *
FROM    mytable
WHERE   guid = HEXTORAW('794AB5396AE5473DA75A9BF8C4AA1F74')

Update:

This query:

SELECT  unique_key
FROM    src_table
WHERE   natural_key1 = :1
        AND natural_key2 = :2
        AND natural_key3 = :3

heavily depends on the type of your fields.

Explicitly cast your variables to the field type, as if from string.

like image 20
Quassnoi Avatar answered Dec 06 '22 20:12

Quassnoi