Oracle
 sql >> Base de données >  >> RDS >> Oracle

Comment fonctionne la comparaison numérique sur la colonne Oracle VARCHAR ?

Comme indiqué dans la SQL Language Reference :

Une conversion implicite est effectuée sur la colonne de la table lorsque les types ne correspondent pas. Cela peut être vu en traçant dans SQL*Plus, avec quelques données factices.

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

Cela fonctionne :

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)

Mais ces erreurs :

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)

Notez la différence dans le filtre ; filter("FOO"='10') contre filter(TO_NUMBER("FOO")=10) . Dans ce dernier cas, en comparant à un nombre, un to_number() est effectuée sur chaque ligne de la table, le résultat de cette conversion est comparé à la valeur fixe. Donc, si l'une des valeurs de caractère ne peut pas être convertie, vous obtiendrez un ORA-01722. La fonction appliquée arrêtera également l'utilisation d'un index, s'il en existe un sur cette colonne.

Là où cela devient intéressant, c'est si vous avez plus d'un filtre. Oracle peut les évaluer dans différents ordres à différents moments, de sorte que vous ne verrez peut-être pas toujours l'ORA-01722, et il apparaîtra parfois. Supposons que vous ayez where foo = 10 and bar = 'X' . Si Oracle pensait pouvoir filtrer les non-X valeurs d'abord, il n'appliquerait que le to_number() à ce qui reste, et cet échantillon plus petit peut ne pas avoir de valeurs non numériques dans foo . Mais si vous avez and bar = 'Y' , le non-Y les valeurs peuvent inclure des valeurs non numériques, ou Oracle peut filtrer sur foo premier , en fonction de la sélectivité des valeurs.

La morale est de ne jamais stocker d'informations numériques en tant que type de caractère.

Je cherchais une référence AskTom pour sauvegarder la morale, et le le premier que j'ai regardé se réfère commodément à l'effet "d'un changement dans l'ordre d'un prédicat" ainsi que de dire "ne stockez pas de nombres dans varchar2".