sql >> Databasteknik >  >> RDS >> Oracle

Hur fungerar numerisk jämförelse på Oracle VARCHAR-kolumnen?

Som anges i SQL Language Reference :

Implicit konvertering utförs i tabellkolumnen när typerna inte matchar. Detta kan ses genom att spåra i SQL*Plus, med lite dummydata.

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

Detta fungerar:

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 detta fel:

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)

Notera skillnaden i filtret; filter("FOO"='10') kontra filter(TO_NUMBER("FOO")=10) . I det senare fallet, jämför man med ett tal, en to_number() utförs mot varje rad i tabellen jämförs resultatet av den konverteringen mot det fasta värdet. Så om något av teckenvärdena inte kan konverteras, får du en ORA-01722. Funktionen som tillämpas kommer också att stoppa ett index som används, om ett sådant finns i den kolumnen.

Där det blir intressant är om du har mer än ett filter. Oracle kan utvärdera dem i olika ordningsföljder vid olika tidpunkter, så du kanske inte alltid ser ORA-01722, och den dyker upp ibland. Säg att du hade where foo = 10 and bar = 'X' . Om Oracle trodde att det kunde filtrera bort icke-X värden först, skulle det bara tillämpa to_number() till det som är kvar, och det mindre urvalet kanske inte har icke-numeriska värden i foo . Men om du har and bar = 'Y' , icke-Y värden kan innehålla icke-numeriska värden, eller Oracle kan filtrera på foo först , beroende på hur selektiva den tror att värdena är.

Moralen är att aldrig lagra numerisk information som en teckentyp.

Jag letade efter en AskTom-referens för att backa upp moralen, och första jag tittade på hänvisar bekvämt till effekten av "en förändring i ordningen för ett predikat" samt att säga "lagra inte nummer i varchar2's".



  1. Få resultat mellan två datum i PostgreSQL

  2. Skriv en fil till SFTP med Oracle PL/SQL

  3. Databashörna:Nybörjarguide till Mysql-lagringsmotorer

  4. Medlemmar rankar endast områden