简体   繁体   English

如何在Android中加快SQLite数据库的插入速度?

[英]How can I speed up SQLite database insertions in Android?

I seem to be getting stuck at 500-1000 ms per insert at some point, when generating large sets of results in a dictionary querying app. 在字典查询应用程序中生成大量结果时,有时似乎每次插入都在500-1000 ms处停留。 Are there any tips or tricks to insertion that I'm not understanding? 有我不了解的插入技巧或窍门吗?

Here is an example of an insert 这是插入的示例

long id = this.getDbAdapter().insertData(
    "combo",
     result.getWord(),
     String.valueOf(result.getNumLetters()),
     String.valueOf(result.getPointsScrabble()),
     String.valueOf(result.getPointsWordsWithFriends())
);
if (id < 0) {
    Log.i(TAG, "Database combo insertion of " + result.getWord() + " unsuccessful :(");
} else {
    Log.i(TAG, "Database combo insertion of " + result.getWord() + " successful :)");

}

Here is my database adapter: I tried to follow Android guidelines... 这是我的数据库适配器:我尝试遵循Android准则...

/**
 * SQLite Database. There are many like it but this one is mine...
 *
 * @author John Doe
 * @author Citizen X
 * @version 0.2 (pre-beta)
 * @see 'http://developer.android.com/reference/android/database/sqlite/package-summary.html'
 * @since 0.1 2015-06-17.
 */

public class ResultsDbAdapter {

    // Debugging tag
    public final static String TAG = "DictionaryDbAdapter";

    // Result types
    private static final String RESULT_TYPE_ANAGRAM = "anagram";
    private static final String RESULT_TYPE_SUBWORD = "subword";
    private static final String RESULT_TYPE_COMBO   = "combo";

    // Handle for helper
    ResultsDbHelper helper;

    // Constructor gets access to inner helper class
    public ResultsDbAdapter(Context context) {
        helper = new ResultsDbHelper(context);
    }

    /* -------------- */
    /* --- Helper --- */
    /* -------------- */

    /**
     * SQLite open helper class
     */
     static class ResultsDbHelper extends SQLiteOpenHelper {

        public static final String TAG = "ResultsDbHelper";

        // If you change the database schema, you must increment the database version.
        public static final int DATABASE_VERSION = 55;
        public static final String DATABASE_NAME = "wordsleuth.db";

        /* ------------------------------ */
        /* --- Contract and constants --- */
        /* ------------------------------ */
        /**
         * Database contract
         */
        public class ResultsDbContract {

            // To prevent someone from accidentally instantiating the contract class,
            // give it an empty constructor.
            public ResultsDbContract() {}

            /* --- Inner class that defines the table contents --- */

            /*
            By implementing the BaseColumns interface, your inner class can inherit a primary key
            field called _ID that some Android classes such as cursor adaptors will expect it to have.
            It's not required, but this can help your database work harmoniously with the Android
            framework.
            */
            public abstract class ResultEntry implements BaseColumns {
                // Table name
                public static final String TABLE_NAME = "results";
                // Table columns
                public static final String UID = "_id";
                public static final String COLUMN_NAME_RESULT_TYPE = "resultype";
                public static final String COLUMN_NAME_WORD = "word";
                public static final String COLUMN_NAME_WORD_LENGTH = "length";
                public static final String COLUMN_NAME_SCRABBLE_POINTS = "scrabblepoints";
                public static final String COLUMN_NAME_WORDS_WITH_FRIENDS_POINTS = "wordspoints";
                // Table text
                private static final String ID_INCREMENT = " INTEGER PRIMARY KEY AUTOINCREMENT";
                private static final String TEXT_TYPE = " TEXT";
                private static final String INTEGER_TYPE = " INTEGER";
                private static final String AND = " AND ";
                private static final String COMMA_SEP = ", ";
                private static final String PARENTHESIS_LEFT = " (";
                private static final String PARENTHESIS_RIGHT = " )";
                // Table commands
                private static final String CREATE_TABLE = "CREATE TABLE ";
                private static final String DROP_TABLE = "DROP TABLE IF EXISTS ";
                private static final String DELETE_FROM_ALL = "DELETE * FROM ";
                // Table creation
                private static final String SQL_CREATE_ENTRIES =
                        CREATE_TABLE + TABLE_NAME + PARENTHESIS_LEFT +             // Create table
                            UID + ID_INCREMENT + COMMA_SEP +                       // _id
                            COLUMN_NAME_RESULT_TYPE + TEXT_TYPE + COMMA_SEP +      // result type
                            COLUMN_NAME_WORD + TEXT_TYPE + COMMA_SEP +             // word
                            COLUMN_NAME_WORD_LENGTH + TEXT_TYPE + COMMA_SEP +      // word length
                            COLUMN_NAME_SCRABBLE_POINTS + TEXT_TYPE + COMMA_SEP +  // sc points
                            COLUMN_NAME_WORDS_WITH_FRIENDS_POINTS + TEXT_TYPE +    // wwf points
                        PARENTHESIS_RIGHT;
                // Table deletion
                private static final String SQL_DELETE_ENTRIES = DROP_TABLE + TABLE_NAME;
                private static final String SQL_DELETE_ALL_ENTRIES = DROP_TABLE + TABLE_NAME;

            }
        }// End contract

        /* ------------------- */
        /* --- Constructor --- */
        /* ------------------- */

        /**
         *
         * @param context - the context
         */
        public ResultsDbHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
            Log.i(TAG, "Database " + DATABASE_NAME + " version " + DATABASE_VERSION + " created");
        }



    /* -------------------------- */
    /* --- Database insertion --- */
    /* -------------------------- */

    /**
     * Insert a result into the database
     * @param resultType
     * @param word
     * @param length
     * @param scrabblePoints
     * @param wordsPoints
     * @return
     */
    public long insertData(
            // Insertion types       // Info
            // ---------------       --------------
            String resultType,       // Result type
            String word,             // The word
            String length,           // The length
            String scrabblePoints,   // Scrabble(TM) point value
            String wordsPoints       // Words With Friends(TM) point value
    ) {
        // Insert data
        SQLiteDatabase db = helper.getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put(COLUMN_NAME_RESULT_TYPE, resultType);
        contentValues.put(COLUMN_NAME_WORD, word);
        contentValues.put(COLUMN_NAME_WORD_LENGTH, length);
        contentValues.put(COLUMN_NAME_SCRABBLE_POINTS, scrabblePoints);
        contentValues.put(COLUMN_NAME_WORDS_WITH_FRIENDS_POINTS, wordsPoints);
        // Returns -1 if fails, otherwise, returns
        return db.insert(TABLE_NAME, null, contentValues);
    }//End insertData()

    // Handle for helper
    ResultsDbHelper helper;

    // Constructor gets access to inner helper class
    public ResultsDbAdapter(Context context) {
        helper = new ResultsDbHelper(context);
    }

    /* -------------- */
    /* --- Helper --- */
    /* -------------- */

    /**
     * SQLite open helper class
     */
     static class ResultsDbHelper extends SQLiteOpenHelper {

        public static final String TAG = "ResultsDbHelper";

        // If you change the database schema, you must increment the database version.
        public static final int DATABASE_VERSION = 55;
        public static final String DATABASE_NAME = "wordsleuth.db";

        /* ------------------------------ */
        /* --- Contract and constants --- */
        /* ------------------------------ */
        /**
         * Database contract
         */
        public class ResultsDbContract {

            // To prevent someone from accidentally instantiating the contract class,
            // give it an empty constructor.
            public ResultsDbContract() {}

            /* --- Inner class that defines the table contents --- */

            /*
            By implementing the BaseColumns interface, your inner class can inherit a primary key
            field called _ID that some Android classes such as cursor adaptors will expect it to have.
            It's not required, but this can help your database work harmoniously with the Android
            framework.
            */
            public abstract class ResultEntry implements BaseColumns {
                // Table name
                public static final String TABLE_NAME = "results";
                // Table columns
                public static final String UID = "_id";
                public static final String COLUMN_NAME_RESULT_TYPE = "resultype";
                public static final String COLUMN_NAME_WORD = "word";
                public static final String COLUMN_NAME_WORD_LENGTH = "length";
                public static final String COLUMN_NAME_SCRABBLE_POINTS = "scrabblepoints";
                public static final String COLUMN_NAME_WORDS_WITH_FRIENDS_POINTS = "wordspoints";
                // Table text
                private static final String ID_INCREMENT = " INTEGER PRIMARY KEY AUTOINCREMENT";
                private static final String TEXT_TYPE = " TEXT";
                private static final String INTEGER_TYPE = " INTEGER";
                private static final String AND = " AND ";
                private static final String COMMA_SEP = ", ";
                private static final String PARENTHESIS_LEFT = " (";
                private static final String PARENTHESIS_RIGHT = " )";
                // Table commands
                private static final String CREATE_TABLE = "CREATE TABLE ";
                private static final String DROP_TABLE = "DROP TABLE IF EXISTS ";
                private static final String DELETE_FROM_ALL = "DELETE * FROM ";
                // Table creation
                private static final String SQL_CREATE_ENTRIES =
                        CREATE_TABLE + TABLE_NAME + PARENTHESIS_LEFT +             // Create table
                            UID + ID_INCREMENT + COMMA_SEP +                       // _id
                            COLUMN_NAME_RESULT_TYPE + TEXT_TYPE + COMMA_SEP +      // result type
                            COLUMN_NAME_WORD + TEXT_TYPE + COMMA_SEP +             // word
                            COLUMN_NAME_WORD_LENGTH + TEXT_TYPE + COMMA_SEP +      // word length
                            COLUMN_NAME_SCRABBLE_POINTS + TEXT_TYPE + COMMA_SEP +  // sc points
                            COLUMN_NAME_WORDS_WITH_FRIENDS_POINTS + TEXT_TYPE +    // wwf points
                        PARENTHESIS_RIGHT;
                // Table deletion
                private static final String SQL_DELETE_ENTRIES = DROP_TABLE + TABLE_NAME;
                private static final String SQL_DELETE_ALL_ENTRIES = DROP_TABLE + TABLE_NAME;

            }
        }// End contract

        /* ------------------- */
        /* --- Constructor --- */
        /* ------------------- */

        /**
         *
         * @param context - the context
         */
        public ResultsDbHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
            Log.i(TAG, "Database " + DATABASE_NAME + " version " + DATABASE_VERSION + " created");
        }


        /**
         * On creation
         * @param db -  the database
         */
        public void onCreate(SQLiteDatabase db) {
            Log.i(TAG, "" + db.getPath() + " created");
            db.execSQL(ResultsDbContract.ResultEntry.SQL_CREATE_ENTRIES);
        }


    /* -------------------------- */
    /* --- Database insertion --- */
    /* -------------------------- */

    /**
     * Insert a result into the database
     * @param resultType
     * @param word
     * @param length
     * @param scrabblePoints
     * @param wordsPoints
     * @return
     */
    public long insertData(
            // Insertion types       // Info
            // ---------------       --------------
            String resultType,       // Result type
            String word,             // The word
            String length,           // The length
            String scrabblePoints,   // Scrabble(TM) point value
            String wordsPoints       // Words With Friends(TM) point value
    ) {
        // Insert data
        SQLiteDatabase db = helper.getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put(COLUMN_NAME_RESULT_TYPE, resultType);
        contentValues.put(COLUMN_NAME_WORD, word);
        contentValues.put(COLUMN_NAME_WORD_LENGTH, length);
        contentValues.put(COLUMN_NAME_SCRABBLE_POINTS, scrabblePoints);
        contentValues.put(COLUMN_NAME_WORDS_WITH_FRIENDS_POINTS, wordsPoints);
        // Returns -1 if fails, otherwise, returns
        return db.insert(TABLE_NAME, null, contentValues);
    }//End insertData()



}

One of the easiest and most effective performance gains you can achieve is to mange The database transaction. 您可以实现的最简单,最有效的性能提升之一就是管理数据库事务。 For more on Transactions. 有关交易的更多信息。

This can be implemented by using something like the following: 可以使用以下类似的方法来实现:

db.beginTransaction();
try {
  InsertData();
  db.setTransactionSuccessful();
} catch {
  //Error in between database transaction 
} finally {
  db.endTransaction();
}

Further by modifying to the standard connection string to the database can yield some improve speeds. 此外,通过将标准连接字符串修改为数据库,可以提高速度。 This is a great resource for various speed improvements, 是进行各种速度改进的好资源,

Reusing a SQLiteStatement between multiple inserts is known to boost insertion speed. 在多个插入之间重用SQLiteStatement可以提高插入速度。

SQLiteDatabase db = helper.getWritableDatabase();
db.beginTransaction();
try {
    final SQLiteStatement insertStatement = db.compileStatement("INSERT INTO x (a,b) VALUES (?, ?);");
    for (Item item : itemsToInsert) {
        insertStatement.clearBindings();
        insertStatement.bindLong(1, item.getA());
        insertStatement.bindString(2, item.getB());
        insertStatement.executeInsert();
    }
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}

Performance gains are usually 2-3x compared to executing a new insert statement for each row, if you insert many of them. 如果您插入很多行,则与为每行执行一个新的插入语句相比,性能提高通常为2-3倍。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM