Xml#parse(String, ContentHandler)
を使用して、かなり大きなXMLファイル(約100キロバイトから数百キロバイトの間で変化します)を解析する必要があります。現在、これを152KBファイルでテストしています。
解析中に、次のような呼び出しを使用してSQLiteデータベースにデータを挿入します:getWritableDatabase().insert(TABLE_NAME, "_id", values)
。このすべてを合わせると、152KBのテストファイルで約80秒かかります(これは約200行の挿入になります)。
すべてのinsertステートメントをコメントアウトすると(ただし、ContentValues
などの作成など、他のすべてを残す)、同じファイルにかかる時間はわずか23秒です。
データベース操作でこのような大きなオーバーヘッドが発生するのは正常ですか?それについて何かできますか?
バッチ挿入を行う必要があります。
擬似コード:
db.beginTransaction();
for (entry : listOfEntries) {
db.insert(entry);
}
db.setTransactionSuccessful();
db.endTransaction();
これにより、アプリへの挿入速度が大幅に向上しました。
更新:
@ Yukuは非常に興味深いブログ投稿を提供しました: sqliteデータベースへの挿入を高速化するためにinserthelperを使用するAndroid
YukuとBrettが言及したInsertHelperは 非推奨 現在(APIレベル17)であるため、Googleが推奨する適切な代替手段はSQLiteStatement。
次のようなデータベース挿入メソッドを使用しました。
database.insert(table, null, values);
深刻なパフォーマンスの問題もいくつか経験した後、次のコードは500回の挿入を14.5秒から270まで高速化しましたms、すごい!
SQLiteStatementの使用方法は次のとおりです。
private void insertTestData() {
String sql = "insert into producttable (name, description, price, stock_available) values (?, ?, ?, ?);";
dbHandler.getWritableDatabase();
database.beginTransaction();
SQLiteStatement stmt = database.compileStatement(sql);
for (int i = 0; i < NUMBER_OF_ROWS; i++) {
//generate some values
stmt.bindString(1, randomName);
stmt.bindString(2, randomDescription);
stmt.bindDouble(3, randomPrice);
stmt.bindLong(4, randomNumber);
long entryID = stmt.executeInsert();
stmt.clearBindings();
}
database.setTransactionSuccessful();
database.endTransaction();
dbHandler.close();
}
Sql insertステートメントをコンパイルすると、速度が向上します。また、すべてが肩にかかっているため、すべてを支えて注射の可能性を防ぐために、より多くの労力が必要になる場合があります。
物事をスピードアップできる別のアプローチは、文書化されていないAndroid.database.DatabaseUtils.InsertHelperクラスです。私の理解では、実際にコンパイルされた挿入ステートメントをラップします。コンパイルされていないトランザクションインサートからコンパイルされたトランザクションインサートに移行すると、大規模な(200K +エントリ)がシンプルなSQLiteインサートの速度が約3倍(挿入あたり2ミリ秒から挿入ごとに0.6ミリ秒)増加しました。
サンプルコード:
SQLiteDatabse db = getWriteableDatabase();
//use the db you would normally use for db.insert, and the "table_name"
//is the same one you would use in db.insert()
InsertHelper iHelp = new InsertHelper(db, "table_name");
//Get the indices you need to bind data to
//Similar to Cursor.getColumnIndex("col_name");
int first_index = iHelp.getColumnIndex("first");
int last_index = iHelp.getColumnIndex("last");
try
{
db.beginTransaction();
for(int i=0 ; i<num_things ; ++i)
{
//need to tell the helper you are inserting (rather than replacing)
iHelp.prepareForInsert();
//do the equivalent of ContentValues.put("field","value") here
iHelp.bind(first_index, thing_1);
iHelp.bind(last_index, thing_2);
//the db.insert() equilvalent
iHelp.execute();
}
db.setTransactionSuccessful();
}
finally
{
db.endTransaction();
}
db.close();
テーブルにインデックスがある場合は、レコードを挿入する前に削除してから、レコードをコミットした後に追加することを検討してください。
ContentProviderを使用する場合:
@Override
public int bulkInsert(Uri uri, ContentValues[] bulkinsertvalues) {
int QueryType = sUriMatcher.match(uri);
int returnValue=0;
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
switch (QueryType) {
case SOME_URI_IM_LOOKING_FOR: //replace this with your real URI
db.beginTransaction();
for (int i = 0; i < bulkinsertvalues.length; i++) {
//get an individual result from the array of ContentValues
ContentValues values = bulkinsertvalues[i];
//insert this record into the local SQLite database using a private function you create, "insertIndividualRecord" (replace with a better function name)
insertIndividualRecord(uri, values);
}
db.setTransactionSuccessful();
db.endTransaction();
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return returnValue;
}
次に、挿入を実行するプライベート関数(まだコンテンツプロバイダー内):
private Uri insertIndividualRecord(Uri uri, ContentValues values){
//see content provider documentation if this is confusing
if (sUriMatcher.match(uri) != THE_CONSTANT_IM_LOOKING_FOR) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
//example validation if you have a field called "name" in your database
if (values.containsKey(YOUR_CONSTANT_FOR_NAME) == false) {
values.put(YOUR_CONSTANT_FOR_NAME, "");
}
//******add all your other validations
//**********
//time to insert records into your local SQLite database
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(YOUR_TABLE_NAME, null, values);
if (rowId > 0) {
Uri myUri = ContentUris.withAppendedId(MY_INSERT_URI, rowId);
getContext().getContentResolver().notifyChange(myUri, null);
return myUri;
}
throw new SQLException("Failed to insert row into " + uri);
}