今天生产环境上碰到个奇怪问题,在oracle数据库一个根据时间范围的查询语句居然执行了二十多分钟。一开始打印了执行sql日志之后就毫无反应了,任何错误都没有。把进程反复重启几次也一样。直到二十分钟后就出现了数据业务处理日志。表的数据量是千万级别,但是时间字段也加了索引的,不至于这么慢吧。查询的时间范围区间也就一分钟。
实在想不出原因在哪里,就写个简单的查询语句试试:
select t.* from IMS_NTF_REMIND_HIS_201711 t where t.SO_DATE>(sysdate-1/24/60) AND t.SO_DATE<sysdate
奇迹发生,这个查询语句瞬间就出结果。马上查代码看查询语句怎么写的。
String sql = "select t.* from " + tableName + " t where " + " t.SO_DATE >=? and t.SO_DATE < ?";
if (logger.isDebugEnabled()) {
logger.debug("执行query的SQL:{}" , sql);
}
conn = ServiceManager.getSession().getConnection(dbAct);
prepare = conn.prepareStatement(sql);
prepare.setTimestamp(1, startTime);
prepare.setTimestamp(2, endTime);
从这个来看貌似也没发现什么问题,两条sql也没有什么区别。看一下数据库SO_DATE字段是date类型。至于为什么使用PreparedStatement.setTimestamp,是因为PreparedStatement.setDate();方法参数java.sql.Date的日期是没有时分秒的,
不符合查询要求。这里想说明一点,oracle数据库字段是date类型的,普通jdbc查询出来的是不带时分秒的,java.util.Date才带时分秒。要有时分秒就要做个转换
HashMap<String, Object> map = new HashMap<String, Object>();
ResultSetMetaData metaData = rs.getMetaData();
int columns = metaData.getColumnCount();
for (int i = 1; i <= columns; i++) {
if ("DATE".equals(metaData.getColumnTypeName(i))) {
map.put(metaData.getColumnName(i), rs.getTimestamp(i));
} else {
map.put(metaData.getColumnName(i), rs.getObject(i));
}
}
rtn.add(map);
言归正传,看到这里估计也猜出来了,oracle字段类型的隐式转换,将date类型转换为timestamp类型。导致索引失效,就会全表扫描,从而查询缓慢。知道问题所在解决就简单了,时间字符串,使用to_date()函数来转换字符串为日期和date做比较。改完之后重新测试查询速度变为了毫秒级别。
总结:
oracle date 和 timestamp类型混用时需要注意索引失效问题。如果oracle数据库字段是date类型,需要时分秒的就用字符串时间格式,使用to_date()函数转换做比较。不需要时分秒的就用java.sql.Date。
网友评论