1

I am testing the capabilities of the device -- to show the customer the size of data that can be stored inside the device, how fast it can be retrieved, how fast the search works, etc.

I am using my content provider to access the product database table with few columns. I have already moved the code to the content provider to avoid the extra communication when inserting the test records. The following code is called via menu from an activity to fill the table with the test content

Uri uri = Uri.parse(DemoContentProvider.PRODUCTS_CONTENT_URI + "/insertdemo");
getContentResolver().insert(uri, null);

The URI is recognized in the .insert method of the content provider and the following private method (of the same content provider) is called to fill the table (notice the 100 thousands of items):

private void insertDemoProducts() {
    for (int i = 1; i <= 100000; ++i) {
        String id = Integer.toString(i);
        insertProduct(id, "Test product " + id, "100", "75.50", "70.27");
    }
}

The inner insertProduct() looks like that:

private void insertProduct(String code, String name, String stock,
                           String price,  String listprice) {
    SQLiteDatabase sqlDB = database.getWritableDatabase();
    ContentValues values = new ContentValues();

    values.put(ProductTable.COLUMN_CODE, code);
    values.put(ProductTable.COLUMN_NAME, name);
    values.put(ProductTable.COLUMN_STOCK, stock);
    values.put(ProductTable.COLUMN_PRICE, price);
    values.put(ProductTable.COLUMN_LISTPRICE, listprice);

    sqlDB.insert(ProductTable.TABLE_PRODUCT, null, values);
}

It works, but it takes "forever". How can I make it faster? What is the fastest method you know to fill the table?

Just some numbers to consider: 1000 items takes about 20 seconds to be created.

4

2 回答 2

1

写入 sqlite 数据库时需要使用事务,否则它将为每个插入保留数据,即将其保存到 sd,这将“永远”。

例如,让 insertProduct 获取产品列表并将它们保存在一个事务中:

private void insertProducts(List<Product> products) {
    try {
        db.beginTransaction();
        for(Product product : products) {
            insertProduct(...);
        }
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
}

这是您可以在现有代码中实现它的方式:

private void insertDemoProducts() {
    SQLiteDatabase sqlDB = database.getWritableDatabase();
    try {
        sqlDB.beginTransaction();
        for (int i = 1; i <= 100000; ++i) {
            String id = Integer.toString(i);
            insertProduct(id, "Test product " + id, "100", "75.50", "70.27");
        }
        sqlDB.setTransactionSuccessful();
    } finally {
        sqlDB.endTransaction();
    }
}
于 2013-05-27T14:28:37.583 回答
0

无论如何,我对接受的问题并不完全满意,因为我不明白添加事务使其更快的原因。

查看 Android 源代码,我发现sqlDB.insert(...)调用insertWithOnConflict(...)和那个使用StringBuilder类构造 SQL 命令的字符串(用问号作为插入值的占位符)。SQLiteStatement只有这样,字符串才会与插入值的数组一起传递给构造函数。这意味着带有 SQL 命令的字符串正在一次又一次地构建。

此外,可以预编译 SQL 命令模板的字符串表示,从而也避免了命令的重复编译。然后.bindXxx.execute方法可用于将想要的记录插入表中。放在一起时,我确实使用了以下代码(除了Dean 建议的外部事务):

        StringBuilder sql = new StringBuilder();
        sql.append("INSERT INTO ");
        sql.append(ProductTable.TABLENAME);
        sql.append("(");
        sql.append(ProductTable.COLUMN_CODE);
        sql.append(",");
        sql.append(ProductTable.COLUMN_NAME);
        sql.append(",");
        sql.append(ProductTable.COLUMN_STOCK);
        sql.append(",");
        sql.append(ProductTable.COLUMN_PRICE);
        sql.append(",");
        sql.append(ProductTable.COLUMN_LISTPRICE);
        sql.append(") VALUES (?, ?, 100, 75.50, 70.27)");

        SQLiteStatement insert = sqldb.compileStatement(sql.toString());

        for (int i = 1; i <= 300000; ++i) {
            insert.bindLong(1, i);
            insert.bindString(2, "Test product " + i);
            insert.execute();
        }

与仅添加事务相比,结果大约快 3 倍。在 Nexus 7 上大约 3 分 15 秒内插入了 30 万条记录。

于 2013-05-28T13:24:11.150 回答