sql >> Base de Datos >  >> RDS >> Oracle

¿Cómo funciona la comparación numérica en la columna Oracle VARCHAR?

Como se indica en la Referencia del lenguaje SQL :

La conversión implícita se realiza en la columna de la tabla cuando los tipos no coinciden. Esto se puede ver rastreando en SQL*Plus, con algunos datos ficticios.

create table t42 (foo varchar2(3 byte));
insert into t42 (foo) values ('10');
insert into t42 (foo) values ('2A');
set autotrace on explain

Esto funciona:

select * from t42 where foo = '10';

FOO
---
10

Execution Plan
----------------------------------------------------------
Plan hash value: 3843907281

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     3 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T42  |     1 |     3 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

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

   1 - filter("FOO"='10')

Note
-----
   - dynamic sampling used for this statement (level=2)

Pero este error:

select * from t42 where foo = 10;

ERROR:
ORA-01722: invalid number



Execution Plan
----------------------------------------------------------
Plan hash value: 3843907281

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     3 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T42  |     1 |     3 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

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

   1 - filter(TO_NUMBER("FOO")=10)

Tenga en cuenta la diferencia en el filtro; filter("FOO"='10') versus filter(TO_NUMBER("FOO")=10) . En el último caso, comparando con un número, un to_number() se realiza con cada fila de la tabla y el resultado de esa conversión se compara con el valor fijo. Entonces, si alguno de los valores de los caracteres no se puede convertir, obtendrá un ORA-01722. La función que se está aplicando también detendrá el uso de un índice, si hay uno presente en esa columna.

Donde se pone interesante es si tienes más de un filtro. Oracle puede evaluarlos en diferentes órdenes en diferentes momentos, por lo que es posible que no siempre vea el ORA-01722 y que aparezca de vez en cuando. Digamos que tenías where foo = 10 and bar = 'X' . Si Oracle pensara que podría filtrar los que no son X valores primero, solo aplicaría to_number() a lo que queda, y esa muestra más pequeña podría no tener valores no numéricos en foo . Pero si tiene and bar = 'Y' , el no Y los valores pueden incluir valores no numéricos, o Oracle podría filtrar en foo primero , dependiendo de cuán selectivos crea que son los valores.

La moraleja es nunca almacenar información numérica como un tipo de carácter.

Estaba buscando una referencia de AskTom para respaldar la moraleja, y primero que miré se refiere convenientemente al efecto de "un cambio en el orden de un predicado", además de decir "no almacenar números en varchar2's".