Oracle VARCHAR列上的数字比较如何工作?

Jam*_*hin 4 oracle plsql oracle11g

我有一个表,其中两列的类型为VARCHAR2(3BYTE)和VARCHAR2(32BYTE).当我执行select查询(where col1=10where col1='10')或(where col2=70001col2='70001')时,在每个where子句集中获取的记录数相同.这是怎么发生的?Oracle如何处理字符串文字和数字常量,并与列数据类型的数据进行比较?

但这对VARCHAR2类型的列(128BYTE)不起作用.查询需要where col3='55555555001'工作并where col3=55555555001抛出ORA-01722错误.

Ale*_*ole 13

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
Run Code Online (Sandbox Code Playgroud)

这有效:

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)
Run Code Online (Sandbox Code Playgroud)

但是这个错误:

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)
Run Code Online (Sandbox Code Playgroud)

注意过滤器的区别; 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中存储数字".