3

データベースに1,000,000行を挿入したいのですが、挿入に時間がかかりすぎます。

たとえば、現在 2055 行で試していますが、このデータをデータベースにアップロードするのに 3 分かかります。この時間は 2055 エントリには長すぎます。

以下は、データベースにデータを挿入する私の方法です。

      public void insert_database(Context context,String field1,String field2,String field3,String field4,String field5,String field6 ,String field7,String field8,String field9,String field10)
{

    try
    {
        //RayAllen_Database.beginTransaction();
        RayAllen_Database.execSQL(" insert or replace into "+ TableName_csv+" values( '"+field1+"' ,'"+field2+"','"+field3+"','"+field4+"','"+field5+"','"+field6+"','"+field7+"','"+field8+"','"+field9+"','"+field10+"');");


    }
    catch(Exception e)
    {
        //Log.i("Database Exception", "Exception");
        e.printStackTrace();
    }

}

と呼ばれる別のクラスで: データの解析: ここでは、csv ファイルを解析し、解析中に:

試す {

CSVReader reader=new CSVReader(new FileReader(filename));
String [] nextLine;

//create database
obj.create_database(context);
obj.OpenDatabase(context);
//reader.readNext();

while ((nextLine=reader.readNext())!=null)
{
          //here I am calling the insert_database function
    }
 }

ここでは、行を 1 つずつ解析し、insert メソッドを呼び出してエントリをデータベースに挿入しています。

しかし、時間がかかりすぎます..どうすればパフォーマンスを向上させることができますか??

4

3 に答える 3

8

「間違った」のではなく正しいことをすべき理由の簡単な例。これは、恐ろしいINSERTパフォーマンスを持つICS4.0.4で実行してテストされました。

まず、列に制約SQLiteOpenHelperのあるテーブルを作成して、時々競合を引き起こす単純なものです。UNIQUE

class SimpleHelper extends SQLiteOpenHelper {
    // InsertHelpers are a really good idea - they format a prepared statement
    // for you automatically.
    InsertHelper mInsert;
    public SimpleHelper(Context context) {
        super(context, "tanika.db", null, 1);
    }
    @Override
    public void onOpen(SQLiteDatabase db) {
        super.onOpen(db);
        mInsert = new InsertHelper(db, "target");
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE target (\n" +
                "_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" +
                "val1 TEXT NOT NULL,\n" +
                "val2 TEXT NOT NULL,\n" +
                "val3 TEXT NOT NULL,\n" +
                // Let's make one unique so we can get some juicy conflicts
                "val4 TEXT NOT NULL UNIQUE\n" +
                ")");
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

古いものにバンドルされているのでActivity、次の簡単なテスト方法を追加します。

long test(final int n) {
    long started = System.currentTimeMillis();
    ContentValues values = new ContentValues();

    for (int i = 0; i < n; i++) {
        values.clear();
        // Every 20th insert, generate a conflict in val4
        String val4 = String.valueOf(started + i);
        if (i % 20 == 0) {
            val4 = "conflict";
        }
        values.put("val1", "Value1");
        values.put("val2", "Value2");
        values.put("val3", "Value3");
        values.put("val4", val4);
        mHelper.mInsert.replace(values);
    }
    return System.currentTimeMillis() - started;
}

ご覧のとおり、これにより20日ごとに競合が発生しINSERTます。呼び出すInsertHelper#replace(..)と、ヘルパーはINSERT OR REPLACEon競合を使用します。

それでは、このテストコードを、周囲のトランザクションがある場合とない場合で実行してみましょう。

class Test1 extends AsyncTask<Integer, Void, Long> {
    @Override
    protected Long doInBackground(Integer... params) {
        return test(params[0]);
    }
    @Override
    protected void onPostExecute(Long result) {
        System.out.println(getClass().getSimpleName() + " finished in " + result + "ms");
    }
}

class Test2 extends AsyncTask<Integer, Void, Long> {
    protected Long doInBackground(Integer... params) {
        SQLiteDatabase db = mHelper.getWritableDatabase();
        db.beginTransaction();
        long started = System.currentTimeMillis();
        try {
            test(params[0]);
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
        return System.currentTimeMillis() - started;
    }
    @Override
    protected void onPostExecute(Long result) {
        System.out.println(getClass().getSimpleName() + " finished in " + result + "ms");
    }
}

すべてが次のように開始されます。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mHelper = new SimpleHelper(this);
    mHelper.getWritableDatabase(); // Forces the helper to initialize.
    new Test1().execute(2055);
    new Test2().execute(2055);
}

そして結果は?トランザクションがない場合、INSERTsは41072msかかります。トランザクションでは、940ミリ秒かかります。つまり、FFSは、InsertHelpersとトランザクションの使用を開始します。

于 2012-05-24T08:32:34.540 に答える
3

オフライン ツールを使用してデータベースにデータを入力し、パッケージのインストール時にインポートすることができます。データベースは、外部 SD カードまたはアプリケーションのアセット フォルダーに保存できます。

これは私がそれを行う方法です:

  1. 次のように、Android Debuger Bridge (adb) を使用して、アプリケーション データベースをローカル フォルダーにコピーしますadb pull /data/data/<your application provider>/databases/yourdatbase.db C:/users/databases/yourdatbase.db

  2. C:/users/databases/yourdatbase.dbお気に入りの GUI/CLI ツールを使用して SQLites データベースに接続し、1 000 000 レコードの作成を完了します。

  3. データが入力されたデータベースを Android 開発環境assetフォルダーにコピーします。

  4. デバイスからアプリケーションをアンインストールして、初めてインストールするときにデータベースが作成されていないことを確認します。

  5. データベースが存在するかどうかをチェックし、存在する場合はそのデータベースを使用するように、SQLiteHepler クラスを変更します。データベースが存在しない場合、ヘルパーは 1 000 000 レコードと一緒にアセット フォルダーからデータベースをコピーします。これが私がやった方法です:

    public class MyDatabaseHelper extends SQLiteOpenHelper {
    
        /*
            Other SQLiteOpenHelper declarations here ...
        */
    
        private static final String DATABASE_NAME   = "application.db";
        private static final String DB_PATH         = "/data/data/" + context.getPackageName() + "/databases/";
    
    
        /*
            Your SQLiteOpenHelper functions/procedures here ...
        */
    
        public boolean isDataBaseExist() {
    
            File dbFile     = new File(DB_PATH + DATABASE_NAME);
            return dbFile.exists();
        }
    
        public void copyDataBase(Context context) throws IOException {
    
            this.getReadableDatabase();
    
            InputStream inFile      = context.getResources().getAssets().open(DATABASE_NAME);
    
            // Path to the just created empty db
            String outFileName      = DB_PATH + DATABASE_NAME;
            OutputStream outFile    = new FileOutputStream(outFileName);
    
            // transfer bytes from the inputfile to the outputfile
            byte[] buffer = new byte[1024];
    
            int length;
    
            while ((length = inFile.read(buffer)) > 0) {
    
                outFile.write(buffer, 0, length);
    
            }
    
            // Close the streams
            outFile.flush();
            outFile.close();
            inFile.close();
        } 
    

このデータベースはアプリでコンパイルされ、最初の起動時にすべてのデータがそこにあります。もっと簡単な方法があるかもしれませんが、これが誰かに役立つことを願っています。

于 2012-05-24T08:21:04.810 に答える
2

sqlite の挿入操作の高速化は、同様のケースを経て、トランザクションを使用して挿入を最適化する方法を示しています。

于 2012-05-24T08:18:08.817 に答える