2010-10-04 3 views
49

Desidero inserire in blocco circa 700 record nel database Android al prossimo aggiornamento. Qual è il modo più efficiente per farlo? Da vari post, so che se utilizzo le istruzioni Insert, dovrei includerle in una transazione. C'è anche un post sull'utilizzo del proprio database, ma ho bisogno di questi dati per andare nel database Android standard della mia app. Si noti che questo sarebbe fatto solo una volta per dispositivo.Inserimento di massa su dispositivo Android

Alcune idee:

  1. mettere un po 'di istruzioni SQL in un file, leggerli in una linea alla volta, e EXEC SQL.

  2. Inserire i dati in un file CSV, o JSON, o YAML, o XML, o qualsiasi altra cosa. Leggere una riga alla volta e fare db.insert().

  3. Capire come eseguire un'importazione e eseguire un'unica importazione dell'intero file.

  4. Creare un database SQL contenente tutti i record, copiarlo sul dispositivo Android e in qualche modo unire i due database.

  5. [EDIT] Metti tutte le istruzioni SQL in un singolo file in res/values ​​come un'unica grande stringa. Quindi leggi loro una riga alla volta ed esegui l'SQL.

Qual è il modo migliore? Ci sono altri modi per caricare i dati? Sono 3 e 4 anche possibili?

risposta

8

Non credo ci sia alcun modo fattibile per completare il n. 3 o il n. 4 nella lista.

Delle altre soluzioni si elencano due che hanno il file di dati contengono SQL diretto e l'altro ha i dati in un formato non SQL.

Tutti e tre funzionerebbero bene, ma il secondo suggerimento di afferrare i dati da un file formattato e costruire da soli l'SQL sembra il più pulito. Se la funzionalità di aggiornamento batch reale viene aggiunta in un secondo momento, il file di dati è ancora utilizzabile, o almeno facilmente elaborabile in una forma utilizzabile. Inoltre, la creazione del file di dati è più semplice e meno soggetta a errori. Infine, avere i dati "grezzi" consentirebbe l'importazione in altri formati di archivio dati.

In ogni caso, è necessario (come si è menzionato) racchiudere i gruppi di inserimenti nelle transazioni per evitare la creazione del giornale di transazione per riga.

33

Ho trovato che per le inserzioni di massa, la classe (apparentemente poco usata) DatabaseUtils.InsertHelper è più veloce rispetto all'utilizzo di SQLiteDatabase.insert. altri

Due ottimizzazioni anche aiutato con le prestazioni della mia app, anche se non può essere opportuno in tutti i casi:

  • fare non bind valori che sono vuoti o null.
  • Se si può essere certi che è sicuro farlo, disattivare temporaneamente il blocco interno del database può anche aiutare le prestazioni.

Ho un blog post con ulteriori dettagli.

+0

Grazie! Questa ottimizzazione ha aumentato l'importazione di un gruppo di record di 2x. –

+0

Ho letto il blog e i commenti. Entrambi hanno fornito dettagli sufficienti per inserire tutti i miei dati entro un paio di secondi. Molte volte meglio di diversi minuti. – Knossos

+4

Ora che 'InsertHelper' è deprecato, siamo costretti a usare' SQLiteStatement'? –

97

Normalmente, ogni volta che viene utilizzato db.insert(), SQLite crea una transazione (e il file di giornale risultante nel filesystem), che rallenta le cose.

Se si utilizza db.beginTransaction() e db.endTransaction() SQLite crea solo un singolo file di journal sul filesystem e quindi esegue il commit di tutti gli inserimenti nello stesso momento, velocizzando notevolmente le cose.

Ecco alcuni pseudo codice da: Batch insert to SQLite database on Android

try 
{ 
    db.beginTransaction(); 

    for each record in the list 
    { 
    do_some_processing(); 

    if (line represent a valid entry) 
    { 
     db.insert(SOME_TABLE, null, SOME_VALUE); 
    } 

    some_other_processing(); 
    } 

    db.setTransactionSuccessful(); 
} 
catch (SQLException e) {} 
finally 
{ 
    db.endTransaction(); 
} 

Se si desidera interrompere una transazione a causa di un errore imprevisto o qualcosa del genere, semplicemente db.endTransaction() senza prima impostare la transazione come successo (db.setTransactionSuccessful()).

Un altro metodo utile è utilizzare db.inTransaction() (restituisce true o false) per determinare se si è attualmente nel mezzo di una transazione.

Documentation here

+0

viene bloccato dopo aver eseguito questa operazione, come sbloccarlo .. – Kishore

+2

Per ulteriori riferimenti al codice: [Inserimento batch nel database SQLite su Android] (http://notes.theorbis.net/2010/02/batch-insert-to-sqlite-on-android.html) –

+1

Tentativo di correggere errori di battitura in 'db.endTransaction()' ma è troppo piccolo una modifica – acw

9

Bene, la mia soluzione per questo è un po 'strano, ma funziona bene ... posso compilare una grossa somma di dati e inserirlo in una sola volta (Inserimento di massa?)

io uso il comando db.execSQL(Query) e costruire il "query" con la seguente affermazione ...

INSERT INTO yourtable SELECT * FROM (
    SELECT 'data1','data2'.... UNION 
    SELECT 'data1','data2'.... UNION 
    SELECT 'data1','data2'.... UNION 
    . 
    . 
    . 
    SELECT 'data1','data2'.... 
) 

l'unico problema è la costruzione della query che può essere di tipo disordinato. Spero che aiuta

+0

Cool. Che grande soluzione creativa.Sembra che sarebbe abbastanza veloce. È? –

+1

Sarebbe così veloce con la classe StringBuilder. –

+0

@Andrew Prat ... Qual è il limite massimo per UNION. per me da questa operazione sindacale posso inserire 500 record in una istruzione di inserimento. se voglio inserire 5000 record in una dichiarazione. sarà possibile? se sì allora come? –

9

Questo esempio di seguito funzionerà perfettamente

String sql = "INSERT INTO " + DatabaseHelper.TABLE_PRODUCT_LIST 
       + " VALUES (?,?,?,?,?,?,?,?,?);"; 

     SQLiteDatabase db = this.getWritableDatabase(); 
     SQLiteStatement statement = db.compileStatement(sql); 
     db.beginTransaction(); 
     for(int idx=0; idx < Produc_List.size(); idx++) { 
      statement.clearBindings(); 
      statement.bindLong(1, Produc_List.get(idx).getProduct_id()); 
      statement.bindLong(2, Produc_List.get(idx).getCategory_id()); 
      statement.bindString(3, Produc_List.get(idx).getName()); 
//   statement.bindString(4, Produc_List.get(idx).getBrand()); 
      statement.bindString(5, Produc_List.get(idx).getPrice()); 
      //statement.bindString(6, Produc_List.get(idx).getDiscPrice()); 
      statement.bindString(7, Produc_List.get(idx).getImage()); 
      statement.bindLong(8, Produc_List.get(idx).getLanguage_id()); 
      statement.bindLong(9, Produc_List.get(idx).getPl_rank()); 
      statement.execute(); 

     } 
     db.setTransactionSuccessful(); 
     db.endTransaction();