Android 數(shù)據(jù)庫批量插入最佳實踐仲翎,吐血推薦

背景

當需要往數(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

  1. 直接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();
            }
        }


    }

  1. 開啟事務(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();
            }
        }
    }
  1. 在方式二的基礎(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

不比不知道啊摧玫, 相差了大約60倍耳奕,果斷選擇方式四,嘿嘿
image.png

最后提醒一句數(shù)據(jù)庫I/O操作記得放到子線程去處理诬像。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屋群,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颅停,更是在濱河造成了極大的恐慌谓晌,老刑警劉巖掠拳,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件癞揉,死亡現(xiàn)場離奇詭異,居然都是意外死亡溺欧,警方通過查閱死者的電腦和手機喊熟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姐刁,“玉大人芥牌,你說我怎么就攤上這事∧羰梗” “怎么了壁拉?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長柏靶。 經(jīng)常有香客問我弃理,道長,這世上最難降的妖魔是什么屎蜓? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任痘昌,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辆苔。我一直安慰自己算灸,他們只是感情好,可當我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布驻啤。 她就那樣靜靜地躺著菲驴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪街佑。 梳的紋絲不亂的頭發(fā)上治泥,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天,我揣著相機與錄音稽煤,去河邊找鬼噪伊。 笑死,一個胖子當著我的面吹牛磁携,可吹牛的內(nèi)容都是我干的褒侧。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼谊迄,長吁一口氣:“原來是場噩夢啊……” “哼闷供!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起统诺,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤歪脏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粮呢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婿失,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年啄寡,在試婚紗的時候發(fā)現(xiàn)自己被綠了豪硅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡挺物,死狀恐怖懒浮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情识藤,我是刑警寧澤砚著,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站痴昧,受9級特大地震影響稽穆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剪个,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一秧骑、第九天 我趴在偏房一處隱蔽的房頂上張望版确。 院中可真熱鬧,春花似錦乎折、人聲如沸绒疗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吓蘑。三九已至,卻和暖如春坟冲,著一層夾襖步出監(jiān)牢的瞬間磨镶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工健提, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留琳猫,地道東北人。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓私痹,卻偏偏與公主長得像脐嫂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子紊遵,可洞房花燭夜當晚...
    茶點故事閱讀 43,666評論 2 350

推薦閱讀更多精彩內(nèi)容