Sie sind auf Seite 1von 8

Oracle Data Type Implicit Conversion Hierarchy

In Oracle, under certain circumstances, an implicit data type conversion precludes the use of indexes. Perhaps you
have a vague idea of what an implicit data type conversion hierarchy is, and you might even ignore the subtlety of
the implicit data type conversion direction within a query predicate expression. Hopefully this article will shed
some light on this concept.

1. Predicate Definition
The most common form of a predicate is:
<expression> <operator> <expression>

For the sake of simplicity we are going to consider implicit conversion during SELECT statements and we’ll
suppose the predicate to be of the following form:
<column> <operator> <literal or bind variable>

Any function explicitly or implicitly applied to the <column> will prevent the index on that column from being
used. This is why the <operator> should always operate on expressions of the same data type. When these two
expressions are of a different data type, an implicit data type conversion occurs. In this situation the direction of
the implicit conversion is of crucial importance. When this conversion is processed from right to left or, when the
data type of the <literal or bind variable> is converted to that of the <column>, then there will very probably be no
impact on the performance of the corresponding query. This is because such a conversion will only be done once
and will also not pre-empt the index on the <column> to be used. If, instead, the implicit conversion is processed
the other way around (meaning that when the <column> data type is aligned with that of the <literal value or bind
variable>) then this might dramatically degrade the performance of the underlying query.

2. Data Type Conversion Hierarchy


Before we get carried away with this new terminology, let’s ask the question: how is this data type implicit
conversion hierarchy decided? In Oracle (and in MSSQL as well) there is an implicit data type conversion
hierarchy which dictates the direction in which Oracle has to apply the data type conversion. For the most common
data types this direction can be summarized as follows: Oracle always converts string to number and string to
date.
VARCHAR → NUMBER
VARCHAR → DATE

Simply put, this means that when a character <column> is compared to an <expression> of a number data type,
then it is the <column> that will undergo an implicit data type conversion, ignoring the existing index on this
column. When a number <column> is compared to an <expression> of character data type then it is the
<expression> that will be converted to the column data type – probably without any significant impact.
By now you’ve hopefully realized you should:
 Always ensure that the operator is applied on columns and expressions of the same data type.
 Or, in the case of implicit data type conversion, ensure that the conversion is done from the data type of the
expression to that of the column and not the other way around.
3. Oracle Data Type Implicit Conversion Example
Now it’s time to put what we’ve learned into action. Here’s how I created the table and the columns to demonstrate
the above conversion hierarchy and direction:
CREATE TABLE t1 (

n1 number,

v1 varchar2(10),

d1 DATE

);

INSERT INTO t1

SELECT rownum n1,

rownum v1,

sysdate + dbms_random.value(0, 365)

FROM dual connect BY LEVEL <= 1e3;

CREATE INDEX t1_n1_idx ON t1 (n1);

CREATE INDEX t1_v1_idx ON t1 (v1);

CREATE INDEX t1_d1_idx ON t1 (d1);

EXEC dbms_stats.gather_table_stats(user, 't1');

As shown below, the table t1 consists of three columns, each of a different data type:
SQL> desc t1

Name Null? Type

---- ----- ------------

N1 NUMBER

V1 VARCHAR2(10)

D1 DATE

In the following query comparing the varchar2 v1 column to a number, the literal value is executed and its
corresponding execution plan is displayed:
SQL> select count(1) from t1 where v1 = 1;

-------------------------------------------

| Id | Operation | Name | Rows |

-------------------------------------------

| 0 | SELECT STATEMENT | | |

| 1 | SORT AGGREGATE | | 1 |

|* 2 | TABLE ACCESS FULL| T1 | 1 |

-------------------------------------------
Predicate Information (identified by operation id):

---------------------------------------------------

2 - filter(TO_NUMBER("V1")=1)

In accordance with what we have seen in the preceding section, when the varchar2 <column> v1 is compared to a
number, Oracle applies the hierarchy and direction of the implicit data type conversion and aligns the v1 datatype
to that of the number literal value. This is clearly visible in the predicate part where a to_number function has been
applied on the v1 column generating a full table scan instead of an index range scan.

Let’s consider the same query where the predicate part is organized the other way around:
SQL> select count(1) from t1 where n1 = '1';

-----------------------------------------------

| Id | Operation | Name | Rows |

-----------------------------------------------

| 0 | SELECT STATEMENT | | |
| 1 | SORT AGGREGATE | | 1 |

|* 2 | INDEX RANGE SCAN| T1_N1_IDX | 1 |

-----------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("N1"=1)

Following the same conversion strategy exposed above, this time Oracle has implicitly converted the data type of
the literal value to that of the column. The column n1, being of a number data type, does not have to comply with
the data type of the <literal> value. Remember the mantra: strings are always converted to numbers and not vice
versa. Notice the absence of the cote surrounding the number 1 in the predicate part indicating the implicit
conversion.

Although the first type of implicit data type conversion processed from the column to the literal value can cause
dramatic deterioration of the query performance, the second kind of data type conversion operating from the literal
value to the column has not pre-empted the index from being used and has not altered the performance of the
corresponding query.

You might ask why Oracle has not managed to change the first query in the same way it did for the second one by
converting the data type of the literal value to that of the column. Something like this:
SQL> select count(1) from t1 where v1 = to_char(1);

-----------------------------------------------

| Id | Operation | Name | Rows |


-----------------------------------------------

| 0 | SELECT STATEMENT | | |

| 1 | SORT AGGREGATE | | 1 |

|* 2 | INDEX RANGE SCAN| T1_V1_IDX | 1 |

-----------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("V1"='1')

Indeed that is a good question, for which I have no answer to other than the above implicit data type conversion
direction which is always processed from string to numbers.
Let’s now consider a date column in the predicate of a similar query:
SQL> alter session set nls_date_format='DD/MM/YYYY HH24:MI:SS';

SQL> select count(1) from t1 where d1 = '26/02/2017 12:22:46';

-----------------------------------------------

| Id | Operation | Name | Rows |

-----------------------------------------------

| 0 | SELECT STATEMENT | | |

| 1 | SORT AGGREGATE | | 1 |

|* 2 | INDEX RANGE SCAN| T1_D1_IDX | 1 |

-----------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("D1"=TO_DATE(' 2017-02-26 12:22:46', 'syyyy-mm-dd hh24:mi:ss'))

Again, as expected, Oracle uses the implicit data type conversion direction to convert the character expression of
the predicate ('26/02/2017 12:22:46') to the date column part (d1) of the same predicate. Since it is the data type of
the expression that has been aligned with that of the column then the index has been used. Slightly changing the
above query to the following one:
SQL> select count(1) from t1 where d1 = TIMESTAMP '2017-02-26 12:22:46';

Yields the following execution plan:

-------------------------------------------

| Id | Operation | Name | Rows |

-------------------------------------------

| 0 | SELECT STATEMENT | | |

| 1 | SORT AGGREGATE | | 1 |

|* 2 | TABLE ACCESS FULL| T1 | 10 |


-------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - filter(INTERNAL_FUNCTION("D1")=TIMESTAMP' 2017-02-26 12:22:46.000000000')

Note how Oracle has applied an INTERNAL_FUNCTION in order to implicitly convert the data type of the d1
column (date) to that of the expression (timestamp). This example has paved the way excellently to announce a
third implicit data type conversion used by Oracle: dates are implicitly converted to timestamp when compared
together in a query predicate:
DATE → TIMESTAMP

In order to avoid such implicit conversion the above timestamp expression has to be converted to a date either by
using a to_date function or a cast operator as shown below:
SQL> select count(1) from t1 where d1 = cast (TIMESTAMP '2017-02-26 12:22:46'as date);

-----------------------------------------------

| Id | Operation | Name | Rows |

-----------------------------------------------

| 0 | SELECT STATEMENT | | |

| 1 | SORT AGGREGATE | | 1 |

|* 2 | INDEX RANGE SCAN| T1_D1_IDX | 1 |

-----------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("D1"=CAST(TIMESTAMP' 2017-02-26 12:22:46.000000000' AS date))

4. MS-SQL data type implicit conversion example


In the preceding sections I wrote that other relational databases are using implicit data type conversion hierarchy as
well. In the below table and query I have demonstrated this conversion direction in MS-SQL Server 2016:
CREATE TABLE t1 (

n1 INTEGER,

v1 CHAR(10),

d1 DATE

);

INSERT INTO t1

SELECT TOP 10000 row_number() OVER (ORDER BY a.NAME) n1,

row_number() OVER (ORDER BY a.NAME) v1,

(getdate() + ROW_NUMBER() OVER (ORDER BY a.NAME)) d1


FROM sys.all_objects a

CROSS JOIN sys.all_objects b;

CREATE CLUSTERED INDEX t1_v1_idx ON t1 (v1);

In the next two queries I am going to select from the above t1 table comparing the v1 character column to an
expression of the same data type (‘1’) in the first query and comparing the same column to an expression of a
different data type in the second query (1):
SELECT count(1) FROM t1 WHERE v1 = '1';

SELECT count(1) FROM t1 WHERE v1 = 1;

The execution plans of the above two queries are shown below:

Notice how in the top execution plan there is an index seek (equivalent to the Oracle index range scan) operation
indicating that the implicit conversion has been done from the expression data type (char) to that of the column
(number), generating a cost of 8%.

Notice as well how in the bottom execution plan there is a much higher cost of 92%, together with a Clustered
Index Scan (the equivalent of Oracle full table scan) because of the conversion happening from the column data
type to that of the expression. Additionally, if you hoover over the yellow symbol with your mouse a new warning
pop-up window will appear indicating that a non-desired implicit data type conversion has been performed.

5. Conclusion
Understanding the hierarchy and the direction in which an implicit data type conversion is going to operate is very
important in order to know whether this conversion will harm the query performance or not. While this article has
not covered all the implicit data type conversion possibilities it has however highlighted few of the most plausible
implicit data type conversions occurring in today’s live systems. The bottom line is that we should always ensure
that the column and the expression on each side of the predicate operator are of the same data type. Or, in case of
forced implicit conversion, ensure that the conversion is done from the data type of the expression to that of the
column and not in the other way around.
The following rules govern implicit data type conversions:
 During INSERT and UPDATE operations, Oracle converts the value to the data type of the affected column.
 During SELECT FROM operations, Oracle converts the data from the column to the type of the target variable.
 When manipulating numeric values, Oracle usually adjusts precision and scale to allow for maximum capacity.
In such cases, the numeric data type resulting from such operations can differ from the numeric data type found
in the underlying tables.
 When comparing a character value with a numeric value, Oracle converts the character data to a numeric value.
 Conversions between character values or NUMBER values and floating-point number values can be inexact,
because the character types and NUMBER use decimal precision to represent the numeric value, and the
floating-point numbers use binary precision.
 When converting a CLOB value into a character data type such as VARCHAR2, or
converting BLOB to RAW data, if the data to be converted is larger than the target data type, then the database
returns an error.
 During conversion from a timestamp value to a DATE value, the fractional seconds portion of the timestamp
value is truncated. This behavior differs from earlier releases of Oracle Database, when the fractional seconds
portion of the timestamp value was rounded.
 Conversions from BINARY_FLOAT to BINARY_DOUBLE are exact.
 Conversions from BINARY_DOUBLE to BINARY_FLOAT are inexact if the BINARY_DOUBLE value uses
more bits of precision that supported by the BINARY_FLOAT.
 When comparing a character value with a DATE value, Oracle converts the character data to DATE.
 When you use a SQL function or operator with an argument of a data type other than the one it accepts, Oracle
converts the argument to the accepted data type.
 When making assignments, Oracle converts the value on the right side of the equal sign (=) to the data type of
the target of the assignment on the left side.
 During concatenation operations, Oracle converts from noncharacter data types to CHAR or NCHAR.
 During arithmetic operations on and comparisons between character and noncharacter data types, Oracle
converts from any character data type to a numeric, date, or rowid, as appropriate. In arithmetic operations
between CHAR/VARCHAR2 and NCHAR/NVARCHAR2, Oracle converts to a NUMBER.
 Most SQL character functions are enabled to accept CLOBs as parameters, and Oracle performs implicit
conversions between CLOB and character types. Therefore, functions that are not yet enabled for CLOBs can
accept CLOBs through implicit conversion. In such cases, Oracle converts the CLOBs
to CHAR or VARCHAR2 before the function is invoked. If the CLOB is larger than 4000 bytes, then Oracle
converts only the first 4000 bytes to CHAR.
 When converting RAW or LONG RAW data to or from character data, the binary data is represented in
hexadecimal form, with one hexadecimal character representing every four bits of RAW data.
 Comparisons between CHAR and VARCHAR2 and between NCHAR and NVARCHAR2 types may entail
different character sets. The default direction of conversion in such cases is from the database character set to
the national character set.
NVL (expr1, expr2)
Users can use the NVL function to convert any data type, but the return value is always the same as the data type of
expr1.

NVL2(expr1, expr2, expr3)


The argument expr1 can have any data type. The arguments expr2 and expr3 can have any (the same) data types
except LONG. If the data types of expr2 and expr3 are different, then Oracle Database implicitly converts one to
the other. If they cannot be converted implicitly, then the database returns an error.

NULLIF (expr1, expr2)


If both arguments are numeric datatypes, then Oracle Database determines the argument with the higher numeric
precedence, implicitly converts the other argument to that datatype, and returns that datatype. If the arguments are
not numeric, then they must be of the same datatype, or Oracle returns an error.

COALESCE (expr1, expr2, ... exprn)


All expressions must be of the same data type. If all occurrences of expr are numeric datatype or any nonnumeric
datatype that can be implicitly converted to a numeric datatype, then Oracle Database determines the argument
with the highest numeric precedence, implicitly converts the remaining arguments to that datatype, and returns that
datatype.

DECODE(col|expression, search1, result1


[, search2, result2,...,]
[, default])
Oracle automatically converts expr and each search value to the datatype of the first search value before comparing.
Oracle automatically converts the return value to the same datatype as the first result. If the first result has the
datatype CHAR or if the first result is null, then Oracle converts the return value to the datatype VARCHAR2.

Das könnte Ihnen auch gefallen