Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Como funciona a comparação numérica na coluna Oracle VARCHAR?


Conforme observado na SQL Language Reference :

A conversão implícita é executada na coluna da tabela quando os tipos não correspondem. Isso pode ser visto rastreando no SQL*Plus, com alguns dados fictícios.
create table t42 (foo varchar2(3 byte));
insert into t42 (foo) values ('10');
insert into t42 (foo) values ('2A');
set autotrace on explain

Isso 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)

Mas este erros:
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)

Observe a diferença no filtro; filter("FOO"='10') versus filter(TO_NUMBER("FOO")=10) . No último caso, comparando com um número, um to_number() está sendo executado em todas as linhas da tabela e o resultado dessa conversão é comparado com o valor fixo. Portanto, se algum dos valores de caractere não puder ser convertido, você obterá um ORA-01722. A função que está sendo aplicada também interromperá o uso de um índice, se houver um presente nessa coluna.

Onde fica interessante é se você tiver mais de um filtro. A Oracle pode avaliá-los em ordens diferentes em momentos diferentes, então você nem sempre verá o ORA-01722, e ele aparecerá algumas vezes. Digamos que você tenha where foo = 10 and bar = 'X' . Se a Oracle pensasse que poderia filtrar o não-X valores primeiro, só aplicaria o to_number() para o que resta, e essa amostra menor pode não ter valores não numéricos em foo . Mas se você tiver and bar = 'Y' , o não-Y os valores podem incluir não numéricos, ou Oracle pode filtrar em foo primeiro , dependendo de quão seletivos são os valores.

A moral é nunca armazenar informações numéricas como um tipo de caractere.

Eu estava procurando uma referência do AskTom para fazer backup da moral e do primeiro que olhei convenientemente se refere ao efeito de "uma mudança na ordem de um predicado", além de dizer "não armazene números em varchar2's".