Baza danych Android SQLite: powolne wstawianie

Muszę przeanalizować dość duży plik XML (waha się od około stu kilobajtów do kilkuset kilobajtów), co robię używając Xml#parse(String, ContentHandler). Obecnie testuję to z plikiem 152KB.

Podczas parsowania wstawiam również dane do bazy danych SQLite za pomocą wywołań podobnych 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 wypowiedzi insert (ale zostawiam we wszystkim innym, np. tworzenie ContentValues itp.) ten sam plik zajmuje tylko 23 sekundy.

Czy to normalne, że operacje baz danych mają tak duże obciążenie? Mogę coś z tym zrobić?

Author: David M, 2010-08-17

5 answers

Należy zrobić wkładki wsadowe.

Pseudocode:

db.beginTransaction();
for (entry : listOfEntries) {
    db.insert(entry);
}
db.setTransactionSuccessful();
db.endTransaction();

To znacznie zwiększyło szybkość wstawek w moich aplikacjach.

Aktualizacja:
@Juku bardzo ciekawy wpis na blogu: Android za pomocą inserthelper do szybszych wstawek do bazy danych SQLite

 182
Author: WarrenFaith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-24 12:28:14

Ponieważ InsertHelper wspomniany przez Juku i Bretta jest przestarzały teraz (poziom API 17), wydaje się, że właściwą alternatywą zalecaną przez Google jest użycie sqlitestatement.

Użyłem Metody wstawiania bazy danych w następujący sposób:

database.insert(table, null, values);

Po tym, jak również doświadczyłem poważnych problemów z wydajnością, poniższy kod przyspieszył moje wstawki 500 z 14,5 sek do tylko 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();
}
 67
Author: qefzec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-28 14:31:30

Kompilacja instrukcji SQL insert pomaga przyspieszyć działanie. Może również wymagać większego wysiłku, aby wszystko uzupełnić i zapobiec ewentualnemu wstrzyknięciu, ponieważ teraz wszystko jest na twoich ramionach.

[1]}innym podejściem, które może również przyspieszyć sprawy, jest słabo udokumentowany android.baza danych.DatabaseUtils./ Align = "left" / Rozumiem, że w rzeczywistości owija skompilowane Oświadczenia insert. Przejście od nie skompilowanych wstawek transakcyjnych do skompilowanych wstawek transakcyjnych było około 3-krotnym zyskiem w prędkość (2ms na wkładkę do .6ms na wkładkę) dla moich dużych (200k+ wpisów), ale prostych wkładek 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();
 13
Author: Brett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-16 21:16:06

Jeśli tabela zawiera indeks, rozważ upuszczenie go przed wstawieniem rekordów, a następnie dodanie go z powrotem po zatwierdzeniu rekordów.

 3
Author: kaD'argo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-18 05:23:18

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 prywatna funkcja do wykonania wstawki (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);


    }
 1
Author: CircuitBreaker716,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-01 17:09:50