`

Oracle数据类型的隐性转换

阅读更多
    今天,以前开发的一个系统出问题,一句类似这样的sql语句:

select * from t where a=1

    其中a是varchar类型。在测试机上跑得好好的,在生产机就报invalid number的错。原因涉及oracle数据库在解析sql语句的时候,对数据进行了类型转换,实际执行的是:

select * from t where to_number(a)=1

    只要表t里面有一条记录的a字段含有不能转换为number的数字,就出错了,而错误当然就是invalid number了。

    此外,由于这样写语句要对表中a字段所有数据都做一次运算,什么索引都没用了,每次搜索都会进行全表扫描和运算,造成性能的下降。所以,就算不出错,也一定要根据数据类型写sql语句。如上例,写成这样就好了:

select * from t where a='1'

    当然,因为这个系统比较小,业务简单,所以是用简单jsp实现的,所有sql语句都自己生成。如果大一点的系统,就可能会用到框架。如何优化对数据库的访问很大程度上都是框架的问题了。

下面是网上搜索到的一篇关于oracle数据类型隐性转换的文章:

ORACLE数据类型的隐性转换

出于优化的目的,在SQL 的编码中我们常提到要避免对字段进行计算,通常情况下字段的计算导致相应索引无法被使用,造成语句执行的开销增大,然而编码中稍有不慎,Oracle自动进行的隐性转换仍有可能进行了我们不希望看到的操作,进而带来严重的后果,索引扫描可能被替换为几千万行的全表扫描,甚至导致业务逻辑发生变化。因此本文列举了代码中常见的几种隐性类型转换,总结并论证其间的规律,以期在开发工作中避免其带来的危害。

为举例创建一个表,包含隐性转换中常见的三种字段类型,并给每个字段建一个索引:

    *********************************************************

create table TT2
(
  NUMCOL     NUMBER,
  CHARCOL    CHAR(10),
  VARCHARCOL VARCHAR2(10)
);

create index IDX_CHAR on TT2 (CHARCOL);
create index IDX_NUM on TT2 (NUMCOL);
create index IDX_VARCHAR on TT2 (VARCHARCOL);
insert into tt2 (numcol,charcol,varcharcol) values (1,'123','1');

*********************************************************

首先将varchar字段与number常量进行比较。

*********************************************************

SQL> select /*+ RULE*/ * from tt2 where varcharcol=1;

NUMCOL  CHARCOL    VARCHARCOL
----------    ----------       ----------
      1    123           1

Execution Plan
---------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (FULL) OF 'TT2'
*********************************************************

这里我们加入了一个RULE 的提示,指定了RBO的优化方式,因此有索引的情况下理所当然优先使用索引,但是恰恰相反,执行计划选择了全表扫描,究其原因,乃是隐性地对 varchar2类型的字段进行了to_number的转换从而屏蔽了索引,实际的Where条件为to_number(varcharcol)=1

变换一下,用number字段与字符常量比较。

*********************************************************

SQL> select /*+ RULE */ * from tt2 where numcol='1';

NUMCOL   CHARCOL  VARCHARCOL
----------   ----------     ----------
         1   123         1

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TT2'
   2    1     INDEX (RANGE SCAN) OF 'IDX_NUM' (NON-UNIQUE)
*********************************************************

执行计划使用了IDX_NUM索引,这次隐性转换没有去转换number字段,而是转换常量'1',实际的where条件变化为where numcol=to_number('1'),字段避免了计算,索引得以被使用。

由此可见,字符与数字相遇时数字类型要厉害一些,Oracle总要偷偷将字符类型进行转换去迁就数字,为了论证这个结论,接上例,再做如下测试。

*********************************************************

SQL> select * from tt2 where varcharcol=1;

    NUMCOL CHARCOL    VARCHARCOL
----------  ----------       ----------
           1  123          1

SQL> update tt2 set varcharcol='1A';
1 row updated.

SQL> select * from tt2 where varcharcol=1;
ERROR at line 1:
ORA-01722: invalid number    

*********************************************************

同样的语句前者执行成功,后者却执行失败,什么都没有变,只是varcharcol字段的值由'1'变成'1A',隐性转换做to_number(varcharcol)操作时,后者无法将字母转换成数字导致ORA-01722错误发生,由此可见这种转换是确实存在的。

接下来,再看看关于Char类型的转换,还有值得我们注意的事情发生。

首先将char类型字段与varchar2进行比较,上例中charcol为char(10)类型,由于char类型的特性,插入的值'123'被自动补齐空格至10位长,因此字段值其实为'123  ',我们执行如下匿名块,并使用SQL_Trace提取执行计划。

*********************************************************

declare

vv varchar2(10):='123';

begin

UPDATE /*+ RULE*/ tt2 set numcol=1 where charcol=vv;

end;

/

Rows     Row Source Operation
-------  ---------------------------------------------------
      0  UPDATE
      0   INDEX RANGE SCAN IDX_CHAR (object id 28748)
*********************************************************

  Rows为0,说明没有行被更新,'123   '与'123'理应为不同的值,这样的结果合理,然而在RBO方式下IDX_CHAR索引被使用,可见并没有对charcol进行隐性的转换。

可是,将char类型字段与字符串常量进行比较呢?

*********************************************************

SQL> select /*+ RULE */ * from tt2 where charcol='123';

    NUMCOL CHARCOL    VARCHARCOL
---------- ---------- ----------
         1 123        1A

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TT2'
   2    1     INDEX (RANGE SCAN) OF 'IDX_CHAR' (NON-UNIQUE)
*********************************************************

居然有数据返回,并且IDX_CHAR 索引被使用,岂不是'123       '与'123'相等,同样的where语句,同样是字符串'123',因为隐性转换产生了不同的逻辑,原来char与字符串常量比较时,隐性将常量按 char的字段类型转换,'123'转换成char(10)则变成了'123    ',看来使用char类型得小心了,两种不同的结果其实就对应着我们在应用开发中将SQL放在存储过程中还是放在中间件或前台代码中。

做了这么多例证,掌握了常用的转换规律后,我们在数据库开发中应该注意些什么呢:

1、 表结构设计中字段的类型以及数据库代码变量的类型应慎用char类型,采用这种类型的数据必须满足两个条件:1)非空;2)有固定长度。

2、 规范编码,尽量避免隐性转换,比较中使用相同类型。

   在代码中构造动态SQL时,对字符串类型字段的比较中常有这种情况发生,实际上构造的语句是将字符与数字进行比较。

    v_SQL:=' ...  where varcharcol='||v_str;

    正确的做法应该是

    v_SQL:=' ...  where varcharcol='''||v_str||'''';

3、 某些标记性或开关意义的字段,取值范围诸如(0,1),(1、2、3)等,尽量使用NUMBER,而不要使用varchar2。

   如果where varcharcol='1'误写为 where varcharcol=1将可能造成严重的性能问题,频繁隐性类型转换还可能造成不可预期的ORA-06512

错误,而 where numcol=1 误写为 where numcol='1'则没有太多不利影响
分享到:
评论

相关推荐

    ORACLE 自动类型转换

    在某些情况下,当不同数据类型的数据需要进行运算或比较时,Oracle会尝试进行自动类型转换。 1. **隐式转换**:这是Oracle自动类型转换的一种形式,它发生在无需显式声明转换的情况下。例如,当一个数字与字符串...

    Oracle与.Net 数据类型映射

    它提供了一个OracleDbType枚举,该枚举定义了所有Oracle数据类型到.NET数据类型的映射。例如,OracleDbType.Number对应.NET的decimal,OracleDbType.Varchar2对应.NET的string,OracleDbType.Date对应.NET的DateTime...

    java、mysql以及oracle数据类型对照表

    标题"java、mysql以及oracle数据类型对照表"揭示了本主题的核心,即比较Java、MySQL和Oracle数据库的数据类型。Java的数据类型主要分为基本类型(如int、double、boolean)和引用类型(如类、接口和数组)。MySQL和...

    mysql和oracle数据库之间的转换工具(支持各种类型数据库)

    1. **数据类型转换**:MySQL和Oracle的数据类型有所不同,如MySQL的VARCHAR2在Oracle中是VARCHAR,日期类型在两者的表示方式也不同。转换工具会自动处理这些差异,确保数据的完整性。 2. **表结构迁移**:包括字段...

    sqlserver-oracle 数据类型对照

    - `bit`在SQL Server中没有直接对应的Oracle数据类型,但可以近似用单字节的`NUMBER(1)`表示。 - `datetime`和`smalldatetime`在SQL Server中对应Oracle的`DATE`,表示日期和时间。 - `decimal`和`numeric`在SQL ...

    oracle字段类型转换的处理

    Oracle数据库支持多种数据类型,包括数值类型(如NUMBER、INTEGER、BINARY_FLOAT等)、字符类型(如VARCHAR2、CHAR、CLOB等)、日期时间类型(如DATE、TIMESTAMP等)以及二进制类型(如RAW、BLOB等)。在实际应用中...

    Oracle与DB2数据类型分类对应说明

    然而,在实际应用中,我们经常需要在 Oracle 和 DB2 之间进行数据类型的转换和对应,这就需要我们对 Oracle 和 DB2 的数据类型有深入的了解。 在本文中,我们将对 Oracle 和 DB2 的数据类型进行分类和对应说明,...

    oracle数据类型.doc

    在Oracle中,数据类型可以分为基本数据类型、对象数据类型和大型对象数据类型。以下是对Oracle主要数据类型的详细说明: 1. **Char(n)**: 这是一种定长字符串数据类型,其中n的范围是1到2000字节。如果未指定长度,...

    Oracle基本数据类型存储格式浅析

    《Oracle基本数据类型存储格式详解》 Oracle数据库中,数据类型的存储格式对于数据库的性能和空间利用率至关重要。本文将深入探讨Oracle的字符类型、数字类型、日期类型、ROWID类型和RAW类型的基本数据类型的存储...

    oracle基本数据类型

    Oracle 基本数据类型 Oracle 中有多种基本数据类型,包括字符类型、数字类型、日期类型、二进制类型等。了解这些数据类型的特点和使用场景,对于数据库设计和开发至关重要。本文将详细介绍 Oracle 中的基本数据类型...

    v512工作室_张利国_Java高端培训系列教材_Oracle实用教程_04章_Oracle数据类型和函数.

    ### Oracle 数据类型详解 #### 4.1 Oracle 数据类型 ##### 4.1.1 概述 在深入了解Oracle数据库中的各种数据类型之前,我们首先需要明确数据类型的基本概念。数据类型是由一组具有相同特性的值及其上定义的操作...

    sql server 和oracle 中数据类型的区别

    对应的Oracle数据类型为`NUMBER(19)`。 2. **binary**: 用于存储二进制数据,固定长度。例如,`binary(50)`表示存储50个字节的二进制数据,在Oracle中对应的类型为`RAW(50)`。 3. **bit**: 用于存储布尔值或单个位的...

    Oracle数据隐式转换规则

    规则 5: 当调用函数或过程等时,如果输入参数的数据类型与函数或者过程定义的参数数据类型不一致,则 Oracle 会将输入参数的数据类型转换为函数或者过程定义的数据类型。例如,假设过程如下定义 p(p_1 number)exec p...

    oracle数据隐式转换规则

    该文档描述了oracle数据的转换的一些规则,如字符和数字的转换,字符和日期的转换

    Oracle实用教程_04章_Oracle数据类型和函数[整理].pdf

    Oracle 数据类型和函数 Oracle 数据类型是指一组性质相同的值的集合以及定义于这个值集合上的一组操作的总称。在 Oracle 数据库中,数据类型可以分为字符型、数值型、日期型和其它类型等几类。 Oracle 数据类型...

    Oracle 10g 操作手册 Oracle数据类型精解

    一、Oracle数据类型详解 在Oracle数据库中,数据类型定义了列可以存储的数据类型。理解这些数据类型对于创建表结构和编写SQL语句至关重要。主要的数据类型包括: 1. **数值型**:NUMBER(p,s)用于存储浮点数,p是总...

    oracle新手入门指导之四——ORACLE数据类型 .txt

    ### Oracle新手入门指导之四——ORACLE数据类型 在Oracle数据库中,数据类型的选择对于确保数据的正确存储、处理效率以及资源的有效利用至关重要。本文将详细介绍Oracle中的各种数据类型及其特性,帮助初学者更好地...

    oracle的数据类型及存储方式 文档

    Oracle 数据类型是数据库管理系统Oracle中用于定义和存储各种数据类型的规则和格式。这些数据类型决定了字段可以存储的数据种类,以及如何存储和处理这些数据。在Oracle中,数据类型分为多种,包括字符类型、数值...

    OracleTOMysql 转换工具

    5. **数据类型转换**:由于Oracle和MySQL支持的数据类型有所不同,转换工具需要能够识别并自动转换这些类型,如NUMBER在Oracle中对应MySQL的DECIMAL或FLOAT。 6. **表结构转换**:转换工具应能识别Oracle的表结构,...

    oracle数据类型.docora复制

    本篇文章将深入探讨Oracle数据类型及其重要性。 首先,Oracle数据类型大致可以分为四大类:数值型、字符型、日期/时间型和二进制型。数值型数据类型包括整数类型(如NUMBER、INTEGER、BINARY_INTEGER)和浮点类型...

Global site tag (gtag.js) - Google Analytics