Zoals vermeld in de SQL-taalreferentie :
Impliciete conversie wordt uitgevoerd op de tabelkolom wanneer de typen niet overeenkomen. Dit kan worden gezien door te traceren in SQL*Plus, met enkele dummy-gegevens.
create table t42 (foo varchar2(3 byte));
insert into t42 (foo) values ('10');
insert into t42 (foo) values ('2A');
set autotrace on explain
Dit werkt:
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)
Maar deze fouten:
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)
Let op het verschil in het filter; filter("FOO"='10')
versus filter(TO_NUMBER("FOO")=10)
. In het laatste geval, vergeleken met een getal, een to_number()
wordt uitgevoerd tegen elke rij in de tabel en het resultaat van die conversie wordt vergeleken met de vaste waarde. Dus als een van de tekenwaarden niet kan worden geconverteerd, krijgt u een ORA-01722. De functie die wordt toegepast, stopt ook het gebruik van een index, als die in die kolom aanwezig is.
Waar het interessant wordt, is als je meer dan één filter hebt. Oracle kan ze op verschillende tijdstippen in verschillende volgorden evalueren, dus u ziet de ORA-01722 misschien niet altijd en soms verschijnt hij. Stel dat je where foo = 10 and bar = 'X'
. had . Als Oracle dacht dat het de niet-X
. zou kunnen filteren waarden eerst, zou het alleen de to_number()
. toepassen naar wat er over is, en die kleinere steekproef heeft mogelijk geen niet-numerieke waarden in foo
. Maar als je and bar = 'Y'
. hebt , de niet-Y
waarden kunnen niet-numerieke waarden bevatten, of Oracle kan filteren op foo
eerste , afhankelijk van hoe selectief hij denkt dat de waarden zijn.
De moraal is om nooit numerieke informatie als tekentype op te slaan.
Ik was op zoek naar een AskTom-referentie om de moraal te ondersteunen, en de eerste waar ik naar keek verwijst gemakshalve naar het effect van "een verandering in de volgorde van een predikaat" en zegt ook "getallen niet opslaan in varchar2's".