4

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

但这不适用于 VARCHAR2(128BYTE) 类型的列。查询需要where col3='55555555001'工作并where col3=55555555001引发 ORA-01722 错误。

4

2 回答 2

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

这有效:

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 中存储数字”的效果。

于 2013-01-24T08:19:26.763 回答
2

如果涉及数字列或值和字符列,Oracle将字符列值转换为数字,然后将数字转换为数字。就好像你写过:

where to_number(col3) = 55555555001

这就是ORA-01722: invalid number如果单行包含无法转换为数值的字符串 (n col3) 时会出现错误的原因。

出于这个原因IS_NUMBER,我们的 Oracle 数据库中有一个函数,它不会导致错误,但对于无法转换为数字的值返回 NULL。然后你可以安全地写:

where is_number(col3) = 55555555001

函数定义为:

CREATE OR REPLACE FUNCTION is_number (p_str IN VARCHAR2)
  RETURN NUMBER
IS
  l_num NUMBER;
BEGIN
  l_num := to_number(p_str);
  RETURN l_num;

EXCEPTION
  WHEN others THEN
    RETURN NULL;
END is_number;
于 2013-01-24T08:18:48.537 回答