1. 程式人生 > 其它 >Java中Oracle時間索引的使用

Java中Oracle時間索引的使用

Oracle中我們經常使用Date欄位型別記錄日期和時間,有的時候還在這個欄位上建立索引。

然後通過Java程式訪問資料庫的時候,我們很自然的類似這樣使用:

select * from table where endDate > ? and endDate < ?

然後通過PreparedStatement預編譯,再通過setTimestamp傳入由java.util.Date轉成java.sql.Timestamp的引數(因為java.sql.Date只有日期,java.sql.Time只有時間,所以我們只能用java.sql.Timestamp型別)。我們會認為這樣應該走索引區間掃描,效率應該是非常高的。

而事實上,Oracle會把sql解釋成如下這樣來執行:

select * from table where TO_TIMESTAMP(endDate) > ? and TO_TIMESTAMP(endDate) < ?; 

為什麼?因為傳入的引數是timestamp型別,Oracle從9.2版本以後支援這種型別,所以Oracle做了這樣的轉換,結果就是這個SQL執行變成了全表掃描。

我們做的試驗,加了一個index hint,強制走時間索引欄位,結果效率也不高,sql執行變成了全索引掃描,和全表掃描沒多大區別。結果效率還是低。

不光直接使用JDBC會是這樣,Spring,iBatis在處理傳入引數是java.util.Date型別的時候,都會使用setTimestamp設定引數,所以都需要注意。

解決辦法(四種解決辦法,推薦方案一):

  1. sql修改成這樣:

    select * from table where endDate>to_date(?,’yyyymmddhh24miss’) and endDate <to_date(?,’yyyymmddhh24miss’);
    

    然後將傳入引數格式化成對應格式的字串在傳入,這樣由Oracle將字串轉成Date型別,就很順利的走索引區間掃描,效率最高。

  2. 在建立資料庫連線的時候增加一個屬性oracle.jdbc.V8Compatible=true,程式碼如下:

    Properties prop=new Properties();
    
    prop.setProperty(“user”,”****”);
    
    prop.setProperty(“password”,”****”);
    
    prop.setProperty(“oracle.jdbc.V8Compatible”,”true”);
    
    Connection connection = DriverManager.getConnection(“jdbc:oracle:thin:@127.0.0.1:1521:test”, prop);
    

    連線池也根據各自的配置方式增加這個屬性即可。目前看來這個屬性引數是處理時間對映關係的,但是還不確定它是否會帶來其他的問題,所以要慎重使用。

  3. 修改資料庫列型別為timestamp型別。

  4. 依據網上資料,Oracle 11g修改了驅動 api,使用方式可能也會有改變。因為無法試驗,所以也不確定具體細節。