Muszę przeanalizować dość duży plik XML (od około stu do kilkuset kilobajtów), którego używam Xml#parse(String, ContentHandler)
. Obecnie testuję to z plikiem 152 KB.
Podczas analizowania, ja też wstawić dane w bazie danych SQLite z wykorzystaniem połączeń podobne do następujących: getWritableDatabase().insert(TABLE_NAME, "_id", values)
. Wszystko to razem zajmuje około 80 sekund dla pliku testowego 152KB (co sprowadza się do wstawienia około 200 wierszy).
Kiedy komentuję wszystkie instrukcje wstawiania (ale zostawiam we wszystkim innym, na przykład tworzenie ContentValues
itp.), Ten sam plik zajmuje tylko 23 sekundy.
Czy to normalne, że operacje na bazie danych mają tak duży narzut? Czy mogę coś z tym zrobić?
Ponieważ InsertHelper wspomniany przez Yuku i Bretta jest obecnie przestarzały (poziom API 17), wydaje się, że właściwą alternatywą zalecaną przez Google jest użycie SQLiteStatement .
Użyłem metody wstawiania do bazy danych w następujący sposób:
database.insert(table, null, values);
Po tym, jak doświadczyłem poważnych problemów z wydajnością, poniższy kod przyspieszył moje 500 wstawek z 14,5 sekundy do zaledwie 270 ms , niesamowite!
Oto jak użyłem 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(); }
źródło
Kompilowanie instrukcji sql insert pomaga przyspieszyć działanie. Może również wymagać więcej wysiłku, aby wszystko podeprzeć i zapobiec możliwemu wstrzyknięciu, ponieważ teraz wszystko jest na twoich barkach.
Innym podejściem, które może przyspieszyć działanie, jest niedokumentowana klasa android.database.DatabaseUtils.InsertHelper. Rozumiem, że w rzeczywistości zawiera skompilowane instrukcje wstawiania. Przejście od nieskompilowanych wstawek transakcyjnych do skompilowanych wstawek transakcyjnych oznaczało około 3-krotny wzrost szybkości (2 ms na wkładkę do 0,6 ms na wstawkę) dla moich dużych (ponad 200 000 wpisów), ale prostych wstawek SQLite.
Przykładowy kod:
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();
źródło
Jeśli tabela ma indeks, rozważ usunięcie go przed wstawieniem rekordów, a następnie dodanie go z powrotem po zatwierdzeniu rekordów.
źródło
Jeśli używasz 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; }
Następnie funkcja prywatna do wstawiania (nadal wewnątrz dostawcy treści):
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); }
źródło