{"id":680,"date":"2010-12-15T15:34:33","date_gmt":"2010-12-15T20:34:33","guid":{"rendered":"http:\/\/www.outofwhatbox.com\/blog\/?p=680"},"modified":"2012-03-19T21:26:55","modified_gmt":"2012-03-20T02:26:55","slug":"android-using-databaseutils-inserthelper-for-faster-insertions-into-sqlite-database","status":"publish","type":"post","link":"https:\/\/www.outofwhatbox.com\/blog\/2010\/12\/android-using-databaseutils-inserthelper-for-faster-insertions-into-sqlite-database\/","title":{"rendered":"Android: Using DatabaseUtils.InsertHelper for faster insertions into SQLite database"},"content":{"rendered":"<p>AndroidOS includes the <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/DatabaseUtils.InsertHelper.html\"><code>DatabaseUtils.InsertHelper<\/code><\/a> class for speeding up insertions into an SQLite database. However, very little documentation or examples seem to be available to show how to use this class. I hope this post will help to make <code>InsertHelper<\/code> a little less mysterious than it apparently has been.<\/p>\n<p>It&#8217;s often the case that bulk insertions are performed in the <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteOpenHelper.html#onCreate(android.database.sqlite.SQLiteDatabase)\"><code>onCreate<\/code><\/a> method of an <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteOpenHelper.html\"><code>SQLiteOpenHelper<\/code><\/a>, so I&#8217;ll use that context for this example. (This has implications on transaction management, which in turn affects performance, as described below.)<\/p>\n<p>Suppose the <code>onCreate<\/code> method currently looks something like this:<\/p>\n<pre class=\"brush: java; title: Old; notranslate\" title=\"Old\">\r\nprivate class DatabaseHelper extends SQLiteOpenHelper {\r\n    @Override\r\n    public void onCreate(SQLiteDatabase db) {\r\n        ContentValues values = new ContentValues();\r\n        while (moreRowsToInsert) {\r\n            \/\/ ... create the data for this row (not shown) ...\r\n\r\n            \/\/ Add the data for each column\r\n            values.put(&quot;Greek&quot;, greekData);\r\n            values.put(&quot;Ionic&quot;, ionicData);\r\n            \/\/ ...\r\n            values.put(&quot;Roman&quot;, romanData);\r\n\r\n            \/\/ Insert the row into the database.\r\n            db.insert(&quot;columnTable&quot;, null, values);\r\n        }\r\n    }\r\n    \/\/...\r\n}\r\n<\/pre>\n<p>Using <code>DatabaseUtils.InsertHelper<\/code>, this would be re-written as:<\/p>\n<pre class=\"brush: java; title: New; notranslate\" title=\"New\">\r\nimport android.database.DatabaseUtils.InsertHelper;\r\n\/\/...\r\nprivate class DatabaseHelper extends SQLiteOpenHelper {\r\n    @Override\r\n    public void onCreate(SQLiteDatabase db) {\r\n        \/\/ Create a single InsertHelper to handle this set of insertions.\r\n        InsertHelper ih = new InsertHelper(db, &quot;columnTable&quot;);\r\n\r\n        \/\/ Get the numeric indexes for each of the columns that we're updating\r\n        final int greekColumn = ih.getColumnIndex(&quot;Greek&quot;);\r\n        final int ionicColumn = ih.getColumnIndex(&quot;Ionic&quot;);\r\n        \/\/...\r\n        final int romanColumn = ih.getColumnIndex(&quot;Roman&quot;);\r\n\r\n        try {\r\n            while (moreRowsToInsert) {\r\n                \/\/ ... Create the data for this row (not shown) ...\r\n\r\n                \/\/ Get the InsertHelper ready to insert a single row\r\n                ih.prepareForInsert();\r\n\r\n                \/\/ Add the data for each column\r\n                ih.bind(greekColumn, greekData);\r\n                ih.bind(ionicColumn, ionicData);\r\n                \/\/...\r\n                ih.bind(romanColumn, romanData);\r\n\r\n                \/\/ Insert the row into the database.\r\n                ih.execute();\r\n            }\r\n        }\r\n        finally {\r\n            ih.close();  \/\/ See comment below from Stefan Anca\r\n        }\r\n    }\r\n    \/\/...\r\n}\r\n<\/pre>\n<p>As this shows, using <code>InsertHelper<\/code> is barely more complicated than using <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteDatabase.html#insert%28java.lang.String,%20java.lang.String,%20android.content.ContentValues%29\"><code>SQLiteDatabase.insert<\/code><\/a>. The major differences are that you need to call     <code>ih.prepareForInsert()<\/code> before adding (&#8220;binding&#8221;) the column data; and you need to obtain each column&#8217;s numeric index, which we get by calling <code>ih.getColumnIndex()<\/code> prior to the loop.<\/p>\n<p>After replacing <code>SQLiteDatabase.insert<\/code> with <code>DatabaseUtils.InsertHelper<\/code>, the database insertion speed went from the equivalent of about 95 rows per second to about 525 rows per second. (&#8220;Equivalent of&#8221;, because the app also spends cycles creating the data to insert. Here, the performance timings are measured using constant data, eliminating that overhead.)<\/p>\n<div class=\"oowbbtw\"><code>InsertHelper <\/code>isn&#8217;t really doing anything magical here. It&#8217;s essentially a wrapper around compiled statements, which you can create yourself using <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteDatabase.html#compileStatement%28java.lang.String%29\"><code>SQLiteDatabase.compileStatement<\/code><\/a>. Most people will probably find <code>InsertHelper<\/code> easier to use, though.<\/div>\n<h3>Other ways to speed up insertions<\/h3>\n<p>In addition to that gain, two more changes then brought the insertion speed to well over 900 rows per second. Whether these <s>tricks<\/s> techniques work for you will depend on your application.<\/p>\n<h4>Don&#8217;t bind empty columns<\/h4>\n<p>In my app, the data for at least 50% of the columns is empty. By skipping the call to <code>ih.bind()<\/code> when the column data is a <code>null<\/code> or empty string, I saw a roughly 30% performance boost.<\/p>\n<h4>Temporarily disable database thread locking<\/h4>\n<p>I&#8217;m loading the database during the <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteOpenHelper.html#onCreate%28android.database.sqlite.SQLiteDatabase%29\"><code>onCreate<\/code><\/a> method of my app&#8217;s <code>SQLiteOpenHelper<\/code>. During this time, it seems safe to assume that only one thread is accessing the database, so I use <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteDatabase.html#setLockingEnabled%28boolean%29\"><code>SQLiteDatabase.setLockingEnabled()<\/code><\/a> to temporarily (<em>let me emphasize that: <strong>temporarily<\/strong><\/em>) disable thread locks within the database API. This yielded about a 35% performance gain:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n    public void onCreate(SQLiteDatabase db) {\r\n    \/\/...\r\n    try {\r\n        \/\/ *Temporarily* (have I emphasized that enough?) disable\r\n        \/\/ thread locking in the database. Be sure to re-enable locking \r\n        \/\/ within a finally block.\r\n        db.setLockingEnabled(false);\r\n        \/\/ ... load the database ...\r\n    }\r\n    finally {\r\n        db.setLockingEnabled(true);\r\n    }\r\n<\/pre>\n<h3>Transactions and performance<\/h3>\n<p>A <a href=\"http:\/\/notes.theorbis.net\/2010\/02\/batch-insert-to-sqlite-on-android.html\">number<\/a> <a href=\"http:\/\/sagistech.blogspot.com\/2010\/07\/notes-on-android-sqlite-bukl-insert.html\">of<\/a> <a href=\"http:\/\/stackoverflow.com\/questions\/3860008\/bulk-insertion-on-android-device\">people<\/a> have cited performance gains through use of explicit transactions in SQLite. However, <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteOpenHelper.html\"><code>SQLiteOpenHelper<\/code><\/a> creates a transaction before invoking its callback methods  (<code>onCreate<\/code>, <code>onUpgrade<\/code>, and <code>onOpen<\/code>), so explicit transaction control is unnecessary within those methods. (<code>SQLiteOpenHelper<\/code> will assume that the transaction was successful unless your method throws an exception.)<\/p>\n<p>You <strong>would<\/strong> need to manage your own transactions if your insertion code is running outside of one of <code>SQLiteOpenHelper<\/code>&#8216;s callback methods. The main APIs for this are <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteDatabase.html#beginTransaction()\"><code>SQLiteDatabase.beginTransaction<\/code><\/a>, <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteDatabase.html#setTransactionSuccessful()\"><code>SQLiteDatabase.setTransactionSuccessful<\/code><\/a>, and <a href=\"http:\/\/developer.android.com\/reference\/android\/database\/sqlite\/SQLiteDatabase.html#endTransaction()\"><code>SQLiteDatabase.endTransaction<\/code><\/a>.<\/p>\n<p>It is possible to nest transactions, but, not surprisingly, this doesn&#8217;t seem to help performance. In fact, I saw a very slight performance degradation when using nested transactions (approximately 1%, probably below the accuracy of the measurements.) I also tried periodically closing the current transaction \u00e2\u20ac\u201d the first of these being the transaction that was opened by <code>SQLiteOpenHelper<\/code> \u00e2\u20ac\u201d then opening a new one. This didn&#8217;t yield much improvement, if any.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using DatabaseUtils.InsertHelper, plus a couple other performance tweaks, yielded a nearly factor of ten performance improvement for bulk database insertions on AndroidOS.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[33],"tags":[51,40,41,43,44,42],"_links":{"self":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts\/680"}],"collection":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/comments?post=680"}],"version-history":[{"count":14,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts\/680\/revisions"}],"predecessor-version":[{"id":685,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts\/680\/revisions\/685"}],"wp:attachment":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/media?parent=680"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/categories?post=680"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/tags?post=680"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}