Android SQLite ডাটাবেস: ধীর সন্নিবেশ


92

আমার একটি মোটামুটি বড় এক্সএমএল ফাইল (প্রায় একশ কিলোবাইট এবং কয়েকশো কিলোবাইটের মধ্যে পৃথক) পার্স করা দরকার, যা আমি ব্যবহার করছি Xml#parse(String, ContentHandler)। আমি বর্তমানে এটি 152 কেবি ফাইল দিয়ে পরীক্ষা করছি।

পার্স সময়, আমি একটি SQLite ডাটাবেসের মধ্যে ডেটাকে নিম্নলিখিত অনুরূপ কল ব্যবহার সন্নিবেশ: getWritableDatabase().insert(TABLE_NAME, "_id", values)। এই সমস্ত একসাথে 152KB পরীক্ষা ফাইলের জন্য প্রায় 80 সেকেন্ড সময় নেয় (যা প্রায় 200 সারি সন্নিবেশ করতে আসে)।

যখন আমি সমস্ত statementsোকানো বিবৃতিগুলি মন্তব্য করি (তবে অন্য সব কিছু ছেড়ে দেওয়া, যেমন তৈরি করা ContentValuesইত্যাদি) একই ফাইলটি কেবল ২৩ সেকেন্ড সময় নেয়।

ডাটাবেস ক্রিয়াকলাপগুলির এত বড় ওভারহেড থাকা কি স্বাভাবিক? আমি কি সে সম্পর্কে কিছু করতে পারি?

উত্তর:


192

আপনার ব্যাচ সন্নিবেশ করা উচিত।

সুডোকোড:

db.beginTransaction();
for (entry : listOfEntries) {
    db.insert(entry);
}
db.setTransactionSuccessful();
db.endTransaction();

এটি আমার অ্যাপ্লিকেশনগুলিতে সন্নিবেশকরণের গতি অত্যন্ত বৃদ্ধি করে।

আপডেট:
@ ইউকু একটি খুব আকর্ষণীয় ব্লগ পোস্ট সরবরাহ করেছে: স্ক্রাইট ডাটাবেসে দ্রুত সন্নিবেশের জন্য অ্যান্ড্রয়েড সন্নিবেশকারী ব্যবহার করে


4
সমস্ত কন্টেন্টভ্যালুগুলি এইভাবে সন্নিবেশ করাতে কেবল এক বা দুটি সময় লাগে। অনেক ধন্যবাদ!
benvd

এক্সএমএল পার্সিং অপারেশনের পুরো সময়কালটি কভার করে, এবং তারপরে এটি শেষে প্রতিশ্রুতিবদ্ধ হওয়া কি দীর্ঘ-চলমান লেনদেন খোলা রাখা নিরাপদ? অথবা অন্তর্ভুক্তির তালিকাটি স্থানীয়ভাবে এক্সএমএল পার্সারের অভ্যন্তরে ক্যাশে করা উচিত এবং তারপরে পার্সিং সম্পূর্ণ হওয়ার পরে একটি স্বল্প-কালীন লেনদেনটি খোলা এবং প্রতিশ্রুতিবদ্ধ হওয়া উচিত?
গ্রাহাম বোরল্যান্ড

4
একটি লেনদেনের সাথে আমার 60 টি সন্নিবেশকে মোড়ক কার্যকারিতা 10x বাড়িয়েছে। এটিকে কোনও লেনদেনের সাথে মোড়ানো এবং একটি প্রস্তুত বিবৃতি (এসকিউএলসাইট) ব্যবহার করে এটি 20x বাড়িয়েছে!
স্টেফস

4
benvd মন্তব্যের জন্য ধন্যবাদ। আমি 20k রেকর্ড ঢোকাতে হয়েছিল, এটা 8mins সম্পর্কে কিন্তু একটি লেনদেন এটি শুধুমাত্র 20 সেকেন্ডের :-) লাগে ব্যবহার করে পরে নিলেন
বিয়ার

4
এই ব্লগ এন্ট্রি প্রায় লুকিয়ে থাকা ইনসার্ট
রেন্ডি

68

যেহেতু ইউকু এবং ব্রেট দ্বারা উল্লিখিত ইনসার্টহেল্পারটি এখন অবচয় করা হয়েছে (এপিআই স্তর 17), এটি গুগলের দ্বারা প্রস্তাবিত সঠিক বিকল্পটি এসকিউএলস্টেটমেন্ট ব্যবহার করছে বলে মনে হয় ।

আমি ডাটাবেস সন্নিবেশ পদ্ধতিটি ব্যবহার করলাম:

database.insert(table, null, values);

আমি কিছু গুরুতর পারফরম্যান্স সমস্যাগুলির পরেও, নিম্নলিখিত কোডটি আমার 500 টি সেন্সর 14.5 সেকেন্ড থেকে কেবল 270 এমএসে বাড়িয়েছে , আশ্চর্যজনক!

আমি এখানে এসকিউএলাইট স্টেটমেন্টটি কীভাবে ব্যবহার করেছি:

private void insertTestData() {
    String sql = "insert into producttable (name, description, price, stock_available) values (?, ?, ?, ?);";

    dbHandler.getWritableDatabase();
    database.beginTransaction();
    SQLiteStatement stmt = database.compileStatement(sql);

    for (int i = 0; i < NUMBER_OF_ROWS; i++) {
        //generate some values

        stmt.bindString(1, randomName);
        stmt.bindString(2, randomDescription);
        stmt.bindDouble(3, randomPrice);
        stmt.bindLong(4, randomNumber);

        long entryID = stmt.executeInsert();
        stmt.clearBindings();
    }

    database.setTransactionSuccessful();
    database.endTransaction();

    dbHandler.close();
}

14
এখানে এড়াতে একটি ধরা: বাইন্ডস্ট্রিংয়ের সূচকটি 1-ভিত্তিক এবং 0 ভিত্তিক নয়
ডিসপ্লে নাম

@ কিয়েফজেক এই সমাধানের জন্য আপনাকে ধন্যবাদ .. এটি আমার অ্যাপে সন্নিবেশের সময়টি
78৮

4
ধন্যবাদ জনাব. ৪০০ মিনিট থেকে ৮ সেকেন্ড অবধি ভर्चার (৮০) সহ প্রতিটি data টি ক্ষেত্রের সাথে 20000 রেকর্ড রয়েছে। আসলে এটি সেরা উত্তর হিসাবে চিহ্নিত করা উচিত, আইএমএইচও।
TomeeNS

4
দুর্দান্ত! ডিভাইস এবং অভ্যন্তরীণ / বাহ্যিক মেমরির উপর নির্ভর করে 4100% থেকে 10400% উন্নতির মধ্যে সন্নিবেশ করাতে 15 টি কলাম সহ একবারে 200 টেস্ট সন্নিবেশগুলিতে আমাদের বেঞ্চমার্ক। পূর্বের পারফরম্যান্সটি আমাদের প্রকল্পটি মাটিতে নামার আগেই নষ্ট হয়ে গিয়েছিল।
ফ্র্যাঙ্ক

13600 সারিগুলির জন্য 15
মিনিট

13

বর্গক্ষেত্র সন্নিবেশ বিবৃতি সংকলন জিনিস দ্রুত গতিতে সাহায্য করে। এটি সমস্ত কিছুর তীরে উপার্জন এবং সম্ভাব্য ইনজেকশন প্রতিরোধ করার জন্য আরও বেশি প্রচেষ্টা প্রয়োজন হতে পারে কারণ এটি এখন আপনার কাঁধে রয়েছে all

জিনিসগুলির গতি বাড়িয়ে তুলতে পারে এমন আরেকটি পদ্ধতি হ'ল আন্ডার-ডকুমেন্টেড অ্যান্ড্রয়েড.ড্যাটাবেস.ডেটবেস ইউটিস.আইনসেটহেলপার ক্লাস। আমার উপলব্ধিটি হ'ল এটি আসলে সংকলিত iledোকানো বিবৃতিগুলিকে আবৃত করে। সংকলিত লেনদেন করা সন্নিবেশ থেকে সংকলিত লেনদেন করা সন্নিবেশগুলিতে যাওয়া আমার বড় (200 কে + এন্ট্রি) তবে সাধারণ এসকিউএলাইট সন্নিবেশগুলির জন্য গতিবেগের প্রায় 2 গুন (প্রতি সন্নিবেশে 2 মিমি প্রতি সন্নিবেশে 2 মিমি) ছিল।

কোডের উদাহরণ:

SQLiteDatabse db = getWriteableDatabase();

//use the db you would normally use for db.insert, and the "table_name"
//is the same one you would use in db.insert()
InsertHelper iHelp = new InsertHelper(db, "table_name");

//Get the indices you need to bind data to
//Similar to Cursor.getColumnIndex("col_name");                 
int first_index = iHelp.getColumnIndex("first");
int last_index = iHelp.getColumnIndex("last");

try
{
   db.beginTransaction();
   for(int i=0 ; i<num_things ; ++i)
   {
       //need to tell the helper you are inserting (rather than replacing)
       iHelp.prepareForInsert();

       //do the equivalent of ContentValues.put("field","value") here
       iHelp.bind(first_index, thing_1);
       iHelp.bind(last_index, thing_2);

       //the db.insert() equilvalent
       iHelp.execute();
   }
   db.setTransactionSuccessful();
}
finally
{
    db.endTransaction();
}
db.close();

এবং কীভাবে iHelp.bind এ কন্টেন্টভ্যালু যুক্ত করা যায় (প্রথম_আডেক্স, জিনিস_1); ?
ভাসিল ভালচেভ

3

যদি টেবিলটিতে কোনও সূচক থাকে তবে রেকর্ডগুলি সন্নিবেশ করার আগে এটিকে বাদ দেওয়ার এবং তারপরে আপনার রেকর্ডগুলি সঞ্চার করার পরে এটি আবার যুক্ত করার বিষয়টি বিবেচনা করুন।


1

যদি কোনও সামগ্রী সরবরাহকারী ব্যবহার করে:

@Override
public int bulkInsert(Uri uri, ContentValues[] bulkinsertvalues) {

    int QueryType = sUriMatcher.match(uri);
    int returnValue=0;
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();

     switch (QueryType) {

         case SOME_URI_IM_LOOKING_FOR: //replace this with your real URI

            db.beginTransaction();

            for (int i = 0; i < bulkinsertvalues.length; i++) {
                //get an individual result from the array of ContentValues
                ContentValues values = bulkinsertvalues[i];
                //insert this record into the local SQLite database using a private function you create, "insertIndividualRecord" (replace with a better function name)
                insertIndividualRecord(uri, values);    
            }

            db.setTransactionSuccessful();
            db.endTransaction();                 

            break;  

         default:
             throw new IllegalArgumentException("Unknown URI " + uri);

     }    

    return returnValue;

}

তারপরে সন্নিবেশ সম্পাদনের জন্য ব্যক্তিগত ফাংশন (এখনও আপনার সামগ্রী সরবরাহকারীর ভিতরে):

       private Uri insertIndividualRecord(Uri uri, ContentValues values){

            //see content provider documentation if this is confusing
            if (sUriMatcher.match(uri) != THE_CONSTANT_IM_LOOKING_FOR) {
                throw new IllegalArgumentException("Unknown URI " + uri);
            }

            //example validation if you have a field called "name" in your database
            if (values.containsKey(YOUR_CONSTANT_FOR_NAME) == false) {
                values.put(YOUR_CONSTANT_FOR_NAME, "");
            }

            //******add all your other validations

            //**********

           //time to insert records into your local SQLite database
           SQLiteDatabase db = mOpenHelper.getWritableDatabase();
           long rowId = db.insert(YOUR_TABLE_NAME, null, values);           

           if (rowId > 0) {
               Uri myUri = ContentUris.withAppendedId(MY_INSERT_URI, rowId);
               getContext().getContentResolver().notifyChange(myUri, null);

               return myUri;
           }


           throw new SQLException("Failed to insert row into " + uri);


    }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.