Jak wspomniano w Informacje o języku SQL :
Konwersja niejawna jest wykonywana w kolumnie tabeli, gdy typy nie są zgodne. Można to zobaczyć, śledząc w SQL*Plus, z pewnymi fikcyjnymi danymi.
create table t42 (foo varchar2(3 byte));
insert into t42 (foo) values ('10');
insert into t42 (foo) values ('2A');
set autotrace on explain
To działa:
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)
Ale te błędy:
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)
Zwróć uwagę na różnicę w filtrze; filter("FOO"='10')
kontra filter(TO_NUMBER("FOO")=10)
. W tym drugim przypadku, porównując z liczbą, to_number()
jest wykonywany dla każdego wiersza w tabeli, wynik tej konwersji jest porównywany z wartością stałą. Więc jeśli którejkolwiek z wartości znaków nie można przekonwertować, otrzymasz ORA-01722. Stosowana funkcja spowoduje również zatrzymanie używanego indeksu, jeśli jest on obecny w tej kolumnie.
Interesujące jest to, że masz więcej niż jeden filtr. Oracle może oceniać je w różnej kolejności w różnym czasie, więc nie zawsze możesz zobaczyć ORA-01722 i czasami się pojawi. Załóżmy, że masz where foo = 10 and bar = 'X'
. Gdyby Oracle myślało, że może odfiltrować znaki inne niż X
najpierw wartości, zastosuje tylko to_number()
do tego, co zostało, a ta mniejsza próbka może nie mieć wartości nieliczbowych w foo
. Ale jeśli masz and bar = 'Y'
, nie-Y
wartości mogą zawierać wartości nieliczbowe, lub Oracle może filtrować na foo
pierwszy , w zależności od tego, jak selektywne są te wartości.
Morał polega na tym, aby nigdy nie przechowywać informacji liczbowych jako typu znaków.
Szukałem odniesienia do AskTom, aby potwierdzić moralność, a pierwszy, na który spojrzałem wygodnie odnosi się do efektu "zmiany w kolejności predykatu", a także do powiedzenia "nie przechowuj numerów w varchar2".