Както е отбелязано в Справочник за SQL език :
Неявното преобразуване се извършва в колоната на таблицата, когато типовете не съвпадат. Това може да се види чрез проследяване в SQL*Plus, с някои фиктивни данни.
create table t42 (foo varchar2(3 byte));
insert into t42 (foo) values ('10');
insert into t42 (foo) values ('2A');
set autotrace on explain
Това работи:
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)
Но тези грешки:
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)
Обърнете внимание на разликата във филтъра; filter("FOO"='10')
спрямо filter(TO_NUMBER("FOO")=10)
. В последния случай, сравняване с число, to_number()
се извършва срещу всеки ред в таблицата, резултатът от това преобразуване се сравнява с фиксираната стойност. Така че, ако някоя от стойностите на знаците не може да бъде преобразувана, ще получите ORA-01722. Приложената функция също ще спре използването на индекс, ако има такъв в тази колона.
Там, където става интересно, е, ако имате повече от един филтър. Oracle може да ги оцени в различни редове по различно време, така че може да не виждате винаги ORA-01722 и понякога ще изскача. Да кажем, че сте имали where foo = 10 and bar = 'X'
. Ако Oracle реши, че може да филтрира неX
първо стойности, ще приложи само to_number()
до това, което е останало, и тази по-малка проба може да няма нечислови стойности в foo
. Но ако имате and bar = 'Y'
, не-Y
стойностите може да включват нечислови стойности, или Oracle може да филтрира по foo
първо , в зависимост от това колко селективни според него са стойностите.
Моралът е никога да не съхранявате числова информация като символен тип.
Търсех препратка към AskTom, за да подкрепя морала, и първият, който погледнах удобно се отнася до ефекта на "промяна в реда на предикат", както и казвайки "не съхранявайте числа във varchar2".