`

一个Date类型的ibatis查询走不上索引的问题

阅读更多

        实际工作中,发现Date类型作为条件查询走不上索引的问题,由于问题完全和http://blog.csdn.net/zldeng19840111/article/details/6721589一样,为简便起见,直接采用它的实例说明。

        以下为简化后的场景:通过时间范围作一个邮件发送数量的统计

java:

import java.util.Date;

public List<Object> listRecentTaskInfoByStatus( Date start, Date end,String sendType) {

        Map<String,String> paraMap = new HashMap<String,String>();
        paraMap.put(TASK_STAT_START_TIME, start);
        paraMap.put(TASK_STAT_END_TIME, end);
        return defaultDao.getObjList(nameSpace.get(sendType), paraMap, "CHANNEL_RECENT_STAT");
}

ibatis:

<select id="SELECT_CHANNEL_RECENT_STAT" resultMap="MtnPlanSendlog_RM_CHANNEL_TASK" parameterClass="java.util.HashMap">
 <![CDATA[
 SELECT /*+index (a SENDLOG_GSEND_IND)*/
     overview_id,
     count(*) as tmp_count
  FROM
     MTN_PLAN_SENDLOG a
  WHERE
     is_deleted = 'n'
     and overview_id is not null
     and GMT_SEND >= #startTime#
     and GMT_SEND < #endTime#
     GROUP BY overview_id
  ]]>
</select>

 

现象:
        sql查询缓慢,DBA观察发现没有走上GMT_SEND的索引

 

原因:
        由于需要小时分秒的信息,我们使用的是java.util.Date,观察发现在数据库端有类似如下函数转换

TO_TIMESTAMP(date_column) = parameter_timestamp

        导致纵然加了hit也走不上索引。

        为什么会触发oracle做隐式转换呢?原因是ibatis在处理传入到数据库的变量时候,如果为java.util.date类型则会自动解析为timestamp类型。具体原因可以参考java.sql.PreparedStatement和相关文档,在此不详述。

public void setParameter(PreparedStatement ps, int i, Object parameter, String jdbcType)   
    throws SQLException {   
  ps.setTimestamp(i, new java.sql.Timestamp(((Date) parameter).getTime()));   
}

        JAVA传下去的是Timestamp,如果数据库中时间字段是date类型,由于timestamp类型的精度比date类型的精度高,根据oracle的策略,所以oracle会对数据库里的date类型做强制隐身转换,将date类型转换为timestamp类型,由于加了函数,那么创建在date类型上的索引将不会使用到;(好像是oracle9i后开始才有的问题)。

 

解决方案:
        经过网上查询,认为可选且比较靠谱的解决方案主要有三种:
a.将数据库的列改为timestamp(风险较太大)
b.使用to_date('2011-03-08 15:45:43.123','yyyy-mm-dd HH24:MI:SS....')

to_date(to_char(#startTime#,'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS');

c.将传入参数由java.util.Date转换为String,然后在ibatis端使用to_date函数解决(改动点太多)

and GMT_SEND >= to_date(#startTime#,'YYYY-MM-DD HH24:MI:SS')

d.给GMT_SEND字段建函数索引(为了走索引而走索引,不是正常的解决问题的办法)

e.cast函数转成Date类型,最终是采用这种方案,对现有程序功能无影响且直接解决问题

and GMT_SEND >= cast(#startTime# as date)

 

附加知识说明:

table_name表的updated_date字段是DATE类型,并建立了索引

select * from table_name t where t.updated_date > to_timestamp('2015-12-01 01:20:30.123','YYYY-MM-DD HH24:MI:SS.ff');

        执行计划是全表扫描的

        而如下SQL:

select * from table_name t where t.updated_date > to_date('2015-12-01 01:20:30.123','yyyy-mm-dd HH24:MI:SS....');

select * from table_name t where t.updated_date > cast(to_timestamp('2015-12-01 01:20:30.123','YYYY-MM-DD HH24:MI:SS.ff') as date);

select * from table_name t where t.updated_date > to_date(to_char(to_timestamp('2015-12-01 01:20:30.123','YYYY-MM-DD HH24:MI:SS.ff'),'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS');

        执行计划是走索引的

        同理,如下SQL由于给t.updated_date多加了一个trunc,也会导致用不上索引而走全表扫描。

select * from table_name t where trunc(t.updated_date) > trunc(to_date('2015-12-01 01:20:30.123','yyyy-mm-dd HH24:MI:SS....'));

        可修改为:

select * from table_name t where t.updated_date > trunc(to_date('2015-12-01 01:20:30.123','yyyy-mm-dd HH24:MI:SS....'));

select * from table_name t where t.updated_date > to_date('2015-12-01 01:20:30.123','yyyy-mm-dd HH24:MI:SS....');
  • 大小: 2 KB
  • 大小: 2.3 KB
分享到:
评论

相关推荐

    ibatis+oracle实例

    在IT行业中,数据库管理和持久化框架是至关重要的技术领域,而`ibatis+oracle`的组合就是这样的一个经典实例。Ibatis,一个轻量级的Java ORM(对象关系映射)框架,允许开发者将SQL语句与Java代码分离,提高了数据库...

    java必备知识点大全.pdf

    Java必备知识点大全的内容非常丰富,涵盖了Java语言的基础知识、常用的框架以及数据库的应用等方面,是每一个Java开发者学习和面试时的重要参考。下面是对文档中各个知识点的详细解析。 基础篇: JDK常用的包:Java...

    北语19春《Oracle数据库开发》作业1234满分答案.pdf

    2. **iBatis框架**:在iBatis框架中,持久化对象之间一对一的关联关系是通过`&lt;one-to-one&gt;`元素定义的。这允许在Java对象和数据库记录之间建立映射关系。正确答案是A。 3. **日期和时间函数**:在SQL查询中,`...

    oracle基础练习.docx

    Oracle数据库是世界上最流行的数据库管理系统之一,尤其在企业级应用中占据重要地位。Oracle数据库系统提供了高效的数据存储和管理能力,支持复杂的数据操作和事务处理。本文将深入探讨Oracle的基础概念和一些常用的...

    java面试知识

    - **唯一索引**:可以为空,但每条记录在这个索引上的值必须唯一。 ##### Preparedstatement和statement的区别 - **PreparedStatement**:预编译SQL语句,提高性能,防止SQL注入。 - **Statement**:每次执行SQL...

    自整理Java关于基础和框架的面试题

    - `request.getSession(true)`:始终返回一个会话,如果不存在则创建一个新会话。 ##### Page和PageContext的区别 - **Page**:表示当前页面或请求的上下文。 - **PageContext**:提供了对页面上可用的变量和对象的...

    Mybatis 3+Mysql 实现批量插入

    综上所述,利用MyBatis结合MySQL实现批量插入不仅可以大幅提高数据处理效率,还能有效降低系统资源消耗,是数据库操作中不可或缺的一项技能。通过上述详细讲解,相信读者已经对如何实现批量插入有了清晰的理解。

    Oracle数据库的基础使用

    Oracle支持多种数据类型,如CHAR、VARCHAR2、NUMBER、DATE以及LOB(BLOB和CLOB)等。 在Oracle中执行SQL语句时,应谨慎处理事务,确保及时提交。此外,使用PL/SQL Developer等工具可以方便地查看表、描述表结构、...

Global site tag (gtag.js) - Google Analytics