如SQL 语言参考中所述:
- 在 SELECT FROM 操作期间,Oracle 将列中的数据转换为目标变量的类型。
- ...
- 在将字符值与数值进行比较时,Oracle 会将字符数据转换为数值。
当类型不匹配时,对表列执行隐式转换。这可以通过在 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()
将对表中的每一行执行 a,将该转换的结果与固定值进行比较。因此,如果任何字符值无法转换,您将得到 ORA-01722。如果该列上存在索引,则正在应用的函数还将停止正在使用的索引。
如果你有多个过滤器,它会变得有趣。Oracle 可能会在不同的时间以不同的顺序评估它们,因此您可能不会总是看到 ORA-01722,它有时会弹出。说你有where foo = 10 and bar = 'X'
。如果 Oracle 认为它可以X
先过滤掉非值,它只会将 应用于to_number()
剩下的内容,并且较小的样本可能在foo
. 但是,如果您有and bar = 'Y'
,则非Y
值可能包括非数字,或者Oracle 可能会foo
首先过滤,这取决于它认为值的选择性。
道德是永远不要将数字信息存储为字符类型。
我正在寻找一个 AskTom 参考来支持道德,我看到的第一个参考是指“谓词顺序的变化”以及“不要在 varchar2 中存储数字”的效果。