Oracle查询隐式类型转换不走索引

本文介绍了在Oracle数据库中,由于隐式类型转换导致查询不使用索引的问题。通过框架如MyBatis进行操作时,这个问题可能会引发性能下降。主要原因是Oracle在比较时若涉及不同类型,会尝试隐式转换,但这种转换可能导致索引无法被有效利用。解决方法包括明确指定数据类型、避免隐式转换和重构查询语句,确保索引的有效使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 框架

mybatis,oracle

2. 问题

昨天DBA发来邮件说有一个SQL引起CPU高负载。查看了一下SQL,根据时间条件查询,时间是有索引的,但是CPU会突然飙高。
SELECT * FROM (
SELECT t1.*,rownum as rn
FROM (
SELECT count(:"SYS_B_0") as COUNT ,T.USER_ID, T.USER_NAME
FROM TEST T
WHERE :"SYS_B_1"=:"SYS_B_2" AND CREATED_TIME >= :4 AND CREATED_TIME <= :5
 group by T.USER_ID,T.USER_NAME ORDER BY COUNT desc ) t1
WHERE rownum <= :6 ) t2
WHERE rn >= :7



然后看一下SQL的执行计划,发现该SQL进行了全表扫描。



可以看到查询的时候使用了过滤器,这就意味着传过来的日期和数据库中的日期格式不对应,可以断定是由于隐式转换导致了时间索引没有用,导致了CPU的飙升。

3. 原因


数据库中的类型是Date类型,传值过去的是Timestamp类型,Java代码里使用的是java.util.Date,配置文件并没有指定类型。那么问题应该就是出在mybaits上了。
mybaits把日期类型直接转化为Timestamp类型,来看下代码:
org.apache.ibatis.type.BaseTypeHandler<T>
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    if (parameter == null) {
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        ps.setNull(i, jdbcType.TYPE_CODE);
      } catch (SQLException e) {
        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
        <span>		</span>"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
        <span>		</span>"Cause: " + e, e);
      }
    } else {
      setNonNullParameter(ps, i, parameter, jdbcType);
    }
  }
org.apache.ibatis.type.DateTypeHandler
  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setTimestamp(i, new Timestamp((parameter).getTime()));
  }
org.apache.ibatis.type.DateOnlyTypeHandler
  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setDate(i, new java.sql.Date((parameter.getTime())));
  }
这里我们看到日期的handler有两个,一个是Timestamp,一个默认是sql.Date。
经过调试代码,发现日期的默认处理handler是DateTypeHandler,那么传到数据库中的值就变成了Timestamp类型了。


主要原因(摘抄)

在9i以后,11g以前的oracle jdbc中存在date类型丢失时间的bug,原因是其jdbc驱动将oracle的date类型处理为java.sql.date 类型,这就丢失了时间部分;事实上,如果是使用ibatis,pojo属性的类型设置为java.util.date,确保 jdbctype不为 date或者time,则避免了这个bug。因为此时ibatis会以java.sql.timestamp来处理该字段。
总的来说就是时间精度处理引起的问题。

4. 解决方法

如果查询是按照日期来的话,可以在参数中指定jdbcType类型为date。
如果需要更精确的时间查询,可以传入字符串,使用to_date()函数进行转化日期。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值