いまさらながら、AndroidのSQLiteのパフォーマンスを比較してみた。
環境
Nexus5, Android 4.4.4
テーブル
こんなかんじ。深い意味はない。
カラム名 | 型 | 制約 |
---|---|---|
id | TEXT | PRIMARY KEY |
name | TEXT | NOT NULL |
address | TEXT | |
count | INTEGER | NOT NULL |
flag | INTEGER | NOT NULL |
1. SQLiteDatabase#insert
おそらく、ほとんどの人が最初に学ぶ方法。
SQLを知らなくても書けるし、わかりやすいが…
db.beginTransaction();
try {
final int columnCount = 5;
final ContentValues values = new ContentValues(columnCount);
for (int i = 0; i < totalCount; ++i) {
values.clear();
values.put("id", "id" + i);
values.put("name", "name" + i);
values.put("address", (String) null);
values.put("count", i);
values.put("flag", 0);
db.insert("table_name", null, values);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
2. SQLiteDatabase#execSQL(単一行)
execSQLを使った方法。
Android Developersには、SELECT/INSERT/UPDATE/DELETE以外のときに使う、との記載があるが、戻り値不要なら使える。
db.beginTransaction();
try {
for (int i = 0; i < totalCount; ++i) {
db.execSQL("INSERT INTO table_name VALUES (?,?,?,?,?)",
new String[] {"id" + i, "name" + i, null, String.valueOf(i), String.valueOf(0)});
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
3. SQLiteDatabase#execSQL(複数行)
ずいぶん長くなってきた。
SQLite3.7.11から使える1回のINSERTで複数行追加できるSQL文。
Android 4.1以降はSQLite3.7.11以降らしいが、それより前のバージョンだとエラーになるので注意。
下記の例では10行ずつINSERTしている。
db.beginTransaction();
try {
final int columnCount = 5;
final int rowCount = 10;
final String[] bindArgs = new String[columnCount * rowCount];
final StringBuilder sql = new StringBuilder("INSERT INTO table_name VALUES (?,?,?,?,?)");
for (int i = 0; i < rowCount - 1; ++i) {
sql.append(", (?,?,?,?,?)");
}
for (int i = 0; i < totalCount; ++i) {
final int index = columnCount * (i % rowCount);
bindArgs[index] = "id" + i;
bindArgs[index + 1] = "name" + i;
bindArgs[index + 2] = null;
bindArgs[index + 3] = String.valueOf(i);
bindArgs[index + 4] = String.valueOf(0);
if (i % rowCount == rowCount - 1) {
db.execSQL(sql.toString(), bindArgs);
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
4. SQLiteDatabase#execSQL(UNION)
SQLite3.7.11以前でも使えるUNIONを用いた1回のINSERTで複数行追加できるSQL文。
Android2.3.3でも使えた。
db.beginTransaction();
try {
final int columnCount = 5;
final int rowCount = 10;
final StringBuilder sql = new StringBuilder("INSERT INTO table_name SELECT ?,?,?,?,?");
final String[] bindArgs = new String[columnCount * rowCount];
for (int i = 0; i < rowCount - 1; ++i) {
sql.append(" UNION ALL SELECT ?,?,?,?,?");
}
for (int i = 0; i < totalCount; ++i) {
final int index = columnCount * (i % rowCount);
bindArgs[index] = "id" + i;
bindArgs[index + 1] = "name" + i;
bindArgs[index + 2] = null;
bindArgs[index + 3] = String.valueOf(i);
bindArgs[index + 4] = String.valueOf(0);
if (i % rowCount == rowCount - 1) {
db.execSQL(sql.toString(), bindArgs);
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
5. SQLiteStatement#executeInsert(単一行)
SQLiteStatementを使った方法。
closeが必要なため、try-finallyが増えるのと、型毎にbindメソッドが異なるのが面倒。
db.beginTransaction();
try {
final SQLiteStatement statement = db.compileStatement("INSERT INTO table_name VALUES (?,?,?,?,?)");
try {
for (int i = 0; i < totalCount; ++i) {
statement.bindString(1, "id" + i);
statement.bindString(2, "name" + i);
statement.bindNull(3);
statement.bindLong(4, i);
statement.bindLong(5, 0);
statement.executeInsert();
}
} finally {
statement.close();
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
6. SQLiteStatement#executeInsert(複数行)
3と同じ複数行INSERTを使った方法。
1回のINSERTで追加する行数が追加する行数の約数になっていないと
2回SQLiteStatementを作成しないといけないのが、大きな欠点。
db.beginTransaction();
try {
final int columnCount = 5;
final int rowCount = 10;
final StringBuilder sql = new StringBuilder("INSERT INTO table_name VALUES (?,?,?,?,?)");
for (int i = 0; i < rowCount - 1; ++i) {
sql.append(", (?,?,?,?,?)");
}
final SQLiteStatement statement = db.compileStatement(sql.toString());
try {
for (int i = 0; i < totalCount; ++i) {
final int index = columnCount * (i % rowCount);
statement.bindString(index + 1, "id" + i);
statement.bindString(index + 2, "name" + i);
statement.bindNull(index + 3);
statement.bindLong(index + 4, i);
statement.bindLong(index + 5, 0);
if (i % rowCount == rowCount - 1) {
statement.executeInsert();
}
} finally {
statement.close();
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
7. SQLiteStatement#executeInsert(UNION)
4と同じUNIONを使った方法。
6と同じく、2回SQLiteStatementを使わないといけない可能性がある、という欠点がある。
db.beginTransaction();
try {
final int columnCount = 5;
final int rowCount = 10;
final StringBuilder sql = new StringBuilder("INSERT INTO table_name SELECT ?,?,?,?,?");
for (int i = 0; i < rowCount - 1; ++i) {
sql.append(" UNION ALL SELECT ?,?,?,?,?");
}
final SQLiteStatement statement = db.compileStatement(sql.toString());
try {
for (int i = 0; i < totalCount; ++i) {
final int index = columnCount * (i % rowCount);
statement.bindString(index + 1, "id" + i);
statement.bindString(index + 2, "name" + i);
statement.bindNull(index + 3);
statement.bindLong(index + 4, i);
statement.bindLong(index + 5, 0);
if (i % rowCount == rowCount - 1) {
statement.executeInsert();
}
} finally {
statement.close();
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
結果
それぞれの方法で100行、1000行、10000行追加した場合の処理時間(msec)は以下の通り。
1回のINSERTで複数行追加できる方法では10行、50行、100行で試した。
単一行については、5(executeInsert)がそれぞれ1(insert)、2(execSQL)の1.6倍、1.3倍程度と最も速い。
複数行についても、6,7(executeInsert)が最も速い。また、追加する全行数が多くなるほど、5に対しても速くなる。今回は、1回あたりの追加行数は50行が一番速いケースが多かったが、テーブルに依存すると思われる。
6,7は上にあげた欠点があるのと、SQL文が長くなりすぎると落ちるため、
基本的には5の方法を使い、1度に大量データを保存するようなアプリでパフォーマンスが求められる場合は6,7の方法を検討しようと思う。
方法 | 1度に追加する行数 | 100行 | 1000行 | 10000行 |
---|---|---|---|---|
1. SQLiteDatabase#insert | 1 | 29.61 | 234.2 | 2166.4 |
2. SQLiteDatabase#execSQL(単一行) | 1 | 21.95 | 175.0 | 1717.9 |
3. SQLiteDatabase#execSQL(複数行) | 10 | 14.43 | 93.2 | 876.8 |
50 | 13.83 | 75.5 | 823.5 | |
100 | 15.21 | 88.6 | 799.8 | |
4. SQLiteDatabase#execSQL(UNION) | 10 | 14.65 | 92.1 | 798.1 |
50 | 14.34 | 83.1 | 784.1 | |
100 | 14.61 | 83.0 | 835.1 | |
5. SQLiteStatement#executeInsert(単一行) | 1 | 17.28 | 126.1 | 1122.3 |
6. SQLiteStatement#executeInsert(複数行) | 10 | 13.27 | 77.9 | 697.2 |
50 | 13.04 | 54.6 | 667.5 | |
100 | 13.5 | 59.8 | 691.7 | |
7. SQLiteStatement#executeInsert(UNION) | 10 | 13.93 | 81.2 | 681.0 |
50 | 13.02 | 97.8 | 661.7 | |
100 | 13.7 | 98.4 | 672.7 |