隐式转换带来的麻烦

oracle的隐式转换有时候会给我们带来很隐蔽的麻烦。看一个简单的例子:

SQL>create table t1(val varchar2(10));

Table created.

SQL>insert into t1 values('XXX');

1 row created.

SQL>insert into t1 values('100');

1 row created.

SQL>commit;

Commit complete.

SQL>select * from t1 where val=100 and rownum<2;
select * from t1 where val=100 and rownum<2
                       *
ERROR at line 1:
ORA-01722: invalid number

再看另一个例子:


SQL>create table t2(val varchar2(10));

Table created.

SQL>insert into t2 values('100');

1 row created.

SQL>insert into t2 values('XXX');

1 row created.

SQL>commit;

Commit complete.

SQL>select * from t2 where val=100 and rownum<2;

VAL
----------
100

查询成功了。仔细观察两个例子,唯一的不同仅仅是两条数据的插入顺序不一样。数据库理论告诉我们,元组(行)在关系(表)中是没有顺序的,我们的程序不应当也不能依赖行的存储顺序。为什么这里会导致不同的结果呢?原因在于val字段类型是varchar2,在和数字100做比较的时候,oracle自动把val转换成了to_number(val)。两个例子都是全表扫描,对t1表,oracle首先读到的第一条记录是‘XXX',to_number自然就失败了,对t2表,oracle读到的第一条记录是'100',to_number成功且条件匹配,由于rownum条件只读取一条,所以立刻退出了,没发生任何错误。如果去掉rownum条件,则两个例子都失败。当然,如果oracle不转换val,而是把100转换成to_char(100),那就没问题了,但oracle认为数字比较总是比字符串比较效率高,所以优先将字符串类型转换为数字类型。

这不仅仅发生在表,索引上同样也会发生类似的情况。所以在设计数据库结构的时候,一定不要为了方便用varchar2去替代其他数据类型,否则一条错误数据就可能让程序崩溃。

查询oracle自己提供的视图也会碰到类似的情况,典型例子就是v$parameter,其中的value字段类型是varchar2,但存储的值有时候是需要转换成数字来进行相应计算的。一个常见的场景是direct path read/write 事件,如果是对TEMP文件进行读写,我们需要这样来获取文件名称:(假设事件P1值是201)

select a.name from v$tempfile a, v$parameter b where b.name='db_files' and a.file# + b.value = 201;

上面的sql是从《Oracle Wait Interface: A Practical Guide to Performance Diagnositics & Tuning》这本书里摘录的,在我的环境下(11.2.0.1.0),得到的唯一结果是ORA-01722。绕过去的办法是改变一下写法:

select a.name from v$tempfile a, v$parameter b where b.name='db_files' and a.file# = 201 - b.value ;

这样能查询到正确结果。但这样做仅仅是通过运气(如果有兴趣可以仔细研究一下两条语句不同的执行计划),正确的做法应该是始终保证比较类型的一致:


select a.name from v$tempfile a, v$parameter b where b.name='db_files' and b.value = to_char( 201 - a.file# ) ;


这样的话就能保证无论什么情况下都能得到正确结果。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值