PreparedStatementは無印Statementより速いのか? part2
記事の内容を鵜呑みにする前に、自分で測定しようと思った。Webアプリケーションで使われる状況を想定した条件にしたつもりだ。。
まず、データベース(もどき)としてMS Accessを使った。テーブルレイアウトは次の通りである。
変数名 | 型 |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
インデックスは主キーであるIDのみに張った。Not Null値はIDとTIMEの2つである。データとしては100,000件用意した。データを生成したプログラムは次である。(Rubyで作った)
require 'win32ole' begin app = WIN32OLE.new("ADODB.Connection") app.open("Driver={Microsoft Access Driver (*.mdb)};DBQ=C:\\Applications\\Apache Group\\jakarta-struts-1.2.4\\webapps\\sample.mdb") 1.upto(100_000) { |i| id = sprintf("%08d", i) sql = "insert into Sample2 (ID, STR1, STR2, STR3, STR4, STR5, STR6, STR7, STR8) " + "values ('#{id}', '#{rand(1000).to_s[0...16]}', '#{rand(2000).to_s[0...16]}', '#{rand(3000).to_s[0...16]}', '#{rand(4000).to_s[0...16]}', '#{rand(5000).to_s[0...16]}', '#{rand(6000).to_s[0...16]}', '#{rand(7000).to_s[0...16]}', '#{rand(8000).to_s[0...16]}');" app.execute sql } rescue STRDRR.puts $! ensure app.close unless app.nil? end
測定方法は、単に次のSQL文を投げただけである。
"select * from Sample2 where STR1 = '" + random.nextInt(1000) + "';"
条件をひとつ付けて該当するレコードを抽出するSQLである。実際にはwhere条件はもっと複雑であろうが、時間計測のためならこれで十分であろう。このSQLを無印StatementとPreparedStatementの場合で1000回実行した。また、SQLのキャッシュの影響を受けないように(多分MS Accessはキャッシュしないと思うが)無印とPreparedを実行する前に、PCを再起動した。時間計測にはSystem.nanoTime()を使用した。測定したプログラムは次である。
import java.util.*; import java.sql.*; import javax.naming.*; import javax.sql.*; public class Main { static final String url = "jdbc:odbc:Sample"; static final Random random = new Random(); static final int MAX = 1000; public static void main(String[] args) throws Exception { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); final long start = System.nanoTime(); if( "type1".equals(args[0]) ) { for(int i=1; i<=MAX; ++i) type1(); }else { for(int i=1; i<=MAX; ++i) type2(); } final long end = System.nanoTime(); System.out.println("経過時間:" + ((end - start) / (1000000.0*MAX)) + "msec / 回"); } static void type1() throws Exception { Connection con = DriverManager.getConnection(url, "", ""); Statement st = con.createStatement(); ResultSet rs = st.executeQuery("select * from Sample2 where STR1 = '" + random.nextInt(1000) + "';"); rs.next(); rs.close(); st.close(); con.close(); } static void type2() throws Exception { Connection con = DriverManager.getConnection(url, "", ""); PreparedStatement st = con.prepareStatement("select * from Sample2 where STR1 = ?;"); st.setInt(1, random.nextInt(1000)); ResultSet rs = st.executeQuery(); rs.next(); rs.close(); st.close(); con.close(); } }
実行結果は、1000回実行したときの平均値で、
無印Statement: 55.429msec / 回 PreparedStatement: 60.134msec / 回
予想通り、というべきかほとんど同じだった。PreparedStatementを毎回生成するため、結局無印Statementと同じようにSQLのパーズを行っているからだろう。