3

我想在数据库中插入 10,00,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();
    }

}

在另一个名为: Parsing Data: 的类中,我正在解析 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
    }
 }

所以这里是逐行解析并调用insert方法将条目插入数据库..

但这太花时间了..我怎样才能提高这个性能?

4

3 回答 3

8

快速示例时间为什么您应该做正确的事情而不是“错误”。这是在 ICS 4.0.4 上运行的测试,它具有可怕的 INSERT 性能。

首先,一个简单的方法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(..)会导致助手使用 a 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。对于事务,它们需要940ms。总之,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. 使用您最喜欢的 GUI/CLI 工具连接到 SQLites 数据库C:/users/databases/yourdatbase.db并完成 1 000 000 条记录的填充。

  3. 将填充的数据库复制到 Android 开发环境asset文件夹。

  4. 现在从设备上卸载您的应用程序,以确保在您第一次安装时没有创建数据库。

  5. 修改您的 SQLiteHepler 类,使其检查数据库是否存在,如果存在则使用该数据库。如果不存在数据库,Helper 会从您的资产文件夹中复制该数据库以及您的 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 回答