背景
當需要往數(shù)據(jù)庫中插入一條數(shù)據(jù)痹扇,那大家肯定沒問題。但需要插入大量數(shù)據(jù)時溯香,怎么辦鲫构? for循環(huán)一條一條插入是可以,但是性能卻一言難盡玫坛。結(jié)合前輩們的經(jīng)驗和自己的實踐對比了幾種插入方式的性能结笨。
插入的幾種方式
- 直接for循環(huán)單條插入
- 開啟事務(wù)后在for循環(huán)單條插入
- 在方式二的基礎(chǔ)上優(yōu)化ContentValues values = new ContentValues();創(chuàng)建次數(shù)
- 直接一句sql語句搞定,終極大法好
具體代碼
前三種比較簡單,直接貼代碼炕吸。 我這里數(shù)據(jù)庫用的是上一篇采坑文章的demo伐憾。鏈接:Android sqlite 跨版本升級時復現(xiàn)報錯 duplicate column
- 直接for循環(huán)單條插入
private void insertOne(SQLiteDatabase db) {
try {
for (int i = 0; i < Num; i++) {
ContentValues values = new ContentValues();
values.put("name", "name: " + i);
values.put("phone", "139-: " + i);
db.insert("student", null, values);
}
} finally {
try {
if (null != db) {
db.endTransaction();
db.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 開啟事務(wù)后在for循環(huán)單條插入
private void insertTwo(SQLiteDatabase db) {
db.beginTransaction();
try {
for (int i = 0; i < Num; i++) {
ContentValues values = new ContentValues();
values.put("name", "name: " + i);
values.put("phone", "139-: " + i);
db.insert("student", null, values);
}
db.setTransactionSuccessful();
} finally {
try {
if (null != db) {
db.endTransaction();
db.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 在方式二的基礎(chǔ)上優(yōu)化ContentValues values = new ContentValues();創(chuàng)建次數(shù)
private void insertThree(SQLiteDatabase db) {
db.beginTransaction();
try {
ContentValues values = new ContentValues();
for (int i = 0; i < Num; i++) {
values.put("name", "name: " + i);
values.put("phone", "139-: " + i);
db.insert("student", null, values);
values.clear();
}
db.setTransactionSuccessful();
} finally {
try {
if (null != db) {
db.endTransaction();
db.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 直接一句sql語句搞定
private void insertFour(SQLiteDatabase db) {
List<InsertBean> insertBeans = getInsertBeans();
db.beginTransaction();
try {
for (InsertBean bean : insertBeans) {
Log.d("dbTest:", "insert,sb.toString(): " + bean.sql);
db.execSQL(bean.sql, bean.bindArgs);
}
db.setTransactionSuccessful();
} catch (Exception e) {
Log.d("dbTest:", "insert赫模,exception: " + e.toString());
} finally {
try {
if (null != db) {
db.endTransaction();
db.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
getInsertBeans()代碼 :
private static int Num = 10000;
private static int Num_interval = 400; //>=500會報錯:
private List<InsertBean> getInsertBeans() {
/// 這里進行分組的原因是 當同時插入>=500條數(shù)據(jù)時树肃,sqlite會報錯:too many SQL variables (code 1
// SQLITE_ERROR): , while compiling: insert or replace into student(
int num = Num / Num_interval;
int left = Num % Num_interval;
List<InsertBean> insertBeans = new ArrayList<>();
for (int k = 0; k < num; k++) {
StringBuilder sb = new StringBuilder("insert or replace into " + "student" + "("
+ "name,"
+ "phone) values");
List<Object> objects = new ArrayList<Object>();
///400條數(shù)據(jù)為一組
for (int i = 0; i < Num_interval; i++) {
int index = k * Num_interval + i;
if (i == Num_interval - 1) {
sb.append("(?,?)");
} else {
sb.append("(?,?),");
}
objects.add("name: " + index);
objects.add("139-: " + index);
}
insertBeans.add(new InsertBean(sb.toString(), objects.toArray()));
}
if (left > 0) {
//追加不能平均分組的數(shù)據(jù)
StringBuilder sb = new StringBuilder("insert or replace into " + "student" + "("
+ "name,"
+ "phone) values");
List<Object> objects = new ArrayList<Object>();
for (int j = 0; j < left; j++) {
int index = j + num * Num_interval;
if (j == left - 1) {
sb.append("(?,?)");
} else {
sb.append("(?,?),");
}
objects.add("name: " + index);
objects.add("139-: " + index);
}
insertBeans.add(new InsertBean(sb.toString(), objects.toArray()));
}
return insertBeans;
}
原理在代碼中寫清楚了。這里提醒下瀑罗,記得看數(shù)據(jù)分組的原因胸嘴。
性能對比
同一臺機型當插入10000條數(shù)據(jù)耗時:
- 方式一 直接for循環(huán)單條插入 cost: 62254ms
- 方式二,開啟事務(wù)循環(huán)插入 cost: 18862ms
- 方式三斩祭,開啟事務(wù)劣像,只用一個ContentValues cost: 16786ms
- 方式四: 開啟事務(wù),分組然后用一句sql插入 cost: 1008ms
image.png
最后提醒一句數(shù)據(jù)庫I/O操作記得放到子線程去處理诬像。