sql >> Database teknologi >  >> RDS >> Oracle

Hvordan fungerer numerisk sammenligning på Oracle VARCHAR kolonne?

Som nævnt i SQL sprogreference :

Implicit konvertering udføres i tabelkolonnen, når typerne ikke matcher. Dette kan ses ved at spore i SQL*Plus, med nogle dummy-data.

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

Dette virker:

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)

Men dette fejler:

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)

Bemærk forskellen i filteret; filter("FOO"='10') versus filter(TO_NUMBER("FOO")=10) . I sidstnævnte tilfælde sammenligner man med et tal, en to_number() udføres mod hver række i tabellen, sammenlignes resultatet af denne konvertering med den faste værdi. Så hvis nogen af ​​tegnværdierne ikke kan konverteres, får du en ORA-01722. Funktionen, der anvendes, vil også stoppe et indeks i at blive brugt, hvis et sådant er til stede i den kolonne.

Hvor det bliver interessant er, hvis du har mere end et filter. Oracle kan evaluere dem i forskellige rækkefølger på forskellige tidspunkter, så du kan ikke altid se ORA-01722, og den dukker nogle gange op. Lad os sige, at du havde where foo = 10 and bar = 'X' . Hvis Oracle troede, at det kunne filtrere ikke-X fra værdier først, ville det kun anvende to_number() til det, der er tilbage, og den mindre prøve har muligvis ikke ikke-numeriske værdier i foo . Men hvis du har and bar = 'Y' , ikke-Y værdier kan omfatte ikke-numeriske værdier, eller Oracle filtrerer muligvis på foo først , alt efter hvor selektive den mener værdierne er.

Moralen er aldrig at gemme numerisk information som en karaktertype.

Jeg ledte efter en AskTom-reference til at sikkerhedskopiere moralen og første jeg så på henviser bekvemt til effekten af ​​"en ændring i rækkefølgen af ​​et prædikat" såvel som at sige "gem ikke numre i varchar2's".



  1. Handling ikke tilladt efter ResultSet lukket (mysql, java)

  2. Oracle-partition efter nøgleord

  3. ORA-12899, mens den faktiske værdilængde er mindre end maksimum

  4. Cast streng til int og brug i Where-sætning