29

Is it possible to take a csv file stored in the res/raw resource directory and use it to populate a table in the sqlite3 database?

My thought was that, if there was a way to do a bulk import for the entire file into the table then that would be cleaner and faster than iterating over each line in the file and executing individual insert statements...

I've found that there is a sqlite import command that allows this: How to import load a .sql or .csv file into SQLite?

...but I'm having trouble applying those statements in my Android application. My first thought was to try something like the following...but no luck:

db.execSQL("CREATE TABLE " + TABLE_NAME + "(id INTEGER PRIMARY KEY, name TEXT)");
db.execSQL(".mode csv");
db.execSQL(".import res/raw/MyFile.csv " + TABLE_NAME); 

Is this possible?

Should I be trying a different approach to populate my database?

UPDATE: I'm marking Josef's response as the answer (bulk insert using transactions) because it works fine and directly answers my question based on my title (thanks Josef). However, I'm am still looking for a way to do a bulk insert in an Android app from csv file into a sqlite3 table using the import statement. If you know how to do this please respond.

Thanks for you answers!

Community
  • 1
  • 1
MoMo
  • 8,149
  • 3
  • 35
  • 31

2 Answers2

31

If you want to package static data with your application, I recommend preparing the database at development time (using any UI or csv-import command you like) and shipping the sqlite file inside the assets folder. You can then simply copy the entire sqlite file onto the device when your application is first run. These posts take you through this idea which is most likely the fastest way to setup a database (file copy speed).

If, for some reason you do need to insert a lot of data at run time, I recommend you look at ways to bulk insert using transactions to speed it up.

Community
  • 1
  • 1
Josef Pfleger
  • 74,165
  • 16
  • 97
  • 99
  • Thanks for the reply Josef, I have tried to ship the sqlite3 .db file in the assets folder of the .apk (like reigndesign post outlines). The issue I'm having with the reigndesign solution is that the onUpgrade() does not get called when my database version # changes. In fact, the only way I can get it to replace the existing database with a newer version is to work around the onUpgrade() and add a statement that explicitly deletes the database just before the createDataBase() is called (which basically forces you to recreate the database every time the Activity is run... =( – MoMo May 22 '10 at 18:04
  • As for using transactions that may be a workable solution. I was able to get the onUpgrad() to be call consistency when I implemented this solution (http://www.screaming-penguin.com/node/7742) but a simple sql import statement is still ideal considering the upgrade and tweaks necessary to get the reigndesign solution to work (i.e. the '_id' column and the 'android_metadata' table). While the tweaks are not difficult to implement I fear it adds unnecessary dependencies to how the Android platform expects their databases to be created. If that were ever to change so would our solution... – MoMo May 22 '10 at 18:15
  • 3
    Shipping with a prebuilt sqlite3 file is NOT good practice. Manufacturers can implement/utilise whatever technology they want under the hood but must adhere to the sqlite3 interfaces - so your prebuilt sqlite3 may not work on some devices. So, best/safest practice is to always let your device create the database, then bulk import to it. – straya Mar 02 '12 at 04:07
  • 1
    There's [an even faster way to do bulk insert](http://stackoverflow.com/a/4464045/64605) than just use transactions. – Juozas Kontvainis Mar 15 '12 at 08:02
14

I spent a lot of time looking for a simple solution and came up with this little bit of code myself. For future people looking for this like I was you can use this:

BufferedReader in = new BufferedReader(your csv file source here);
String reader = "";
while ((reader = in.readLine()) != null){
    String[] RowData = reader.split(",");
    date = RowData[0];
    value = RowData[1];
    ContentValues values = new ContentValues();
    values.put(CsvProvider.DATE, date);
    values.put(CsvProvider.VALUE, value);
    getContentResolver().insert(CsvProvider.CONTENT_URI, values);
}
in.close();

My CSV file has no titles and only has 2 columns but more than two columns should not be a problem. Just remember to specify what is splitting your columns and for each column add another RowData[#](you have to start with 0). You want to make sure whatever you are going to do with each line is done before you call in.close(). I am using a content provider but you can really do whatever you want with the data like append it to a String[] or whatever else.

While I am using an input stream you can point the BufferedReader to wherever you want. As long as the BufferedReader can read it then it will work.

mhu
  • 17,720
  • 10
  • 62
  • 93
Opy
  • 2,119
  • 3
  • 18
  • 22
  • 1
    that seperator is a pain. I dont know why people use it to begin with. I am trying to remove it from a file and it just stays. Is there any way to use a comma or other character or is that from some web server somewhere? – Opy Nov 22 '10 at 21:54
  • Nope.. it has to be "|", as there are 700-800k records and Im using "," as data..:(.. anyways I got it working... I used StringTokenizer for splitting.. still im not even close to solve the problem.. I did split files into 1mb each.. Im using AsyncTask and able to insert 5k records.. buffer is breaking after that... it's strange it's taking really long just to copy and create db...I have not even started working on app yet... :( – k-thorat Nov 23 '10 at 16:58
  • 1
    Yea, I have 2049 records to import and it takes up to 60 seconds to import. That's way too long. I've tried transactions but I get nullpointerexceptions. But I do actually get the data into my db. – Opy Nov 23 '10 at 20:39
  • how long does it take to get the 5k records in? – Opy Nov 24 '10 at 14:26
  • how to remove "" around data?? – Umesh Feb 21 '14 at 14:09