Android SQLite Upgrade without losing data - android

I have created a SQLite database successfully and it works fine. However when the onUpgrade method is called, I'd like to do so without losing data. The app I'm developing is a quiz app. Simply, when the onCreate method is called I create and prepopulate a database with questions, answers etc. The last column is whether they have set the question as a favourite or not. What I would like to do is that when the onUpgrade method is called, I'd like to temporarily save that one column, drop the whole database, recreate it with any edits I've made to old questions and add any new questions then re-add back the questions that they set as favourites.
So one option I tried was the following:
db.execSQL("ALTER TABLE quiz RENAME TO temp_quiz");
onCreate(db);
db.execSQL("INSERT INTO quiz (favouries) SELECT favourites FROM temp_quiz");
db.execSQL("DROP TABLE IF EXISTS temp_quiz");
However this doesn't work owing to the fact INSERT INTO just adds new rows rather than replacing the existing rows. I have also tried REPLACE INTO, INSERT OR REPLACE INTO and
db.execSQL("INSERT INTO quiz (_id, favouries) SELECT _id, favourites FROM temp_quiz");
of which none work.
Currently I do have it set up to work by altering the name of the table, calling the onCreate(db) method and then setting up a cursor which reads each row and uses the db.update() method as shown below:
int place = 1;
int TOTAL_NUMBER_OF_ROWS = 500;
while (place < TOTAL_NUMBER_OF_ROWS) {
String[] columns = new String[] { "_id", ..........., "FAVOURITES" };
// not included all the middle columns
Cursor c = db.query("temp_quiz", columns, "_id=" + place, null, null, null, null);
c.moveToFirst();
String s = c.getString(10);
// gets the value from the FAVOURITES column
ContentValues values = new ContentValues();
values.put(KEY_FLAG, s);
String where = KEY_ROWID + "=" + place;
db.update(DATABASE_TABLE, values, where, null);
place++;
c.close();
}
However whilst this works it is extremely slow and will only get worse as my number of questions increases. Is there a quick way to do all this?
Thank you! P.S. Ideally it should only update the row if the row is present. So if in an upgrade I decide to remove a question, it should take this into account and not add a new row if the row doesn't contain any other data. It might be easier to get it to remove rows that don't have question data rather than prevent them being added.

changed it to:
db.execSQL("UPDATE new_quiz SET favourites = ( SELECT old_quiz.favourites
FROM old_quiz WHERE new_quiz._id = old_quiz._id) WHERE EXISTS
( SELECT old_quiz.favourites FROM old_quiz WHERE new_quiz._id = old_quiz._id)");
Which works :D

public class DataHelper extends SQLiteOpenHelper {
private static final String dbName="dbName";
private Context context;
private SQLiteDatabase db;
private final static int version = 1;
public static final String SurveyTbl = "CREATE TABLE SurveyTbl (SurveyId TEXT PRIMARY KEY, Idref TEXT, SurveyDate TEXT)";
public DataHelper(Context context) {
super(context, dbName, null, version);
this.db = getWritableDatabase();
this.context = context;
Log.i("", "********************DatabaseHelper(Context context)");
}
#Override
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL(SurveyTbl);
} catch (Exception e) {
Log.i("", "*******************onCreate");
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
try {
db.execSQL("ALTER TABLE HandpumpSurveyTbl ADD COLUMN NalYozna TEXT");
} catch (Exception e) {
Log.i("", ""+e);
}
onCreate(db);
}
}

I didn't get to see your Quiz table schema, but I assume it has fields like "question", "answer", "favorites", and some kind of a unique primary key to identify each question, which I will just call rowId for now.
// after renaming the old table and adding the new table
db.execSQL("UPDATE new_quiz SET new_quiz.favorites = old_quiz.favorites where new_quiz.rowId = old_quiz.rowId");
That will update only the rows of the new quiz table that match the old quiz table, and set the favorites value from the old quiz table.
I assume you have some kind of a unique identifier to identify each question, so instead of the rowId above, you'll use that (question number or something).

For who don't know yet how to upgrade the version of the SQLite when upgrading the database schema for example, use the method needUpgrade(int newVersion)!
My code:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
if(newVersion>oldVersion){
db.execSQL(scriptUpdate);
db.needUpgrade(newVersion);
}
}

ALTER TABLE mytable ADD COLUMN mycolumn TEXT
In your onUpgrade method, it would look something like this:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String upgradeQuery = "ALTER TABLE mytable ADD COLUMN mycolumn TEXT";
if (newVersion>oldVersion)
db.execSQL(upgradeQuery);
}

Example, how to drop a table and create a new table without losing data by using a temporary table:
db.execSQL("CREATE TEMPORARY TABLE temp_table (_id INTEGER PRIMARY KEY AUTOINCREMENT, col_1 TEXT, col_2 TEXT);");
db.execSQL("INSERT INTO temp_table SELECT _id, col_1, col_2 FROM old_table");
db.execSQL("CREATE TABLE new_table (_id INTEGER PRIMARY KEY AUTOINCREMENT, col_1 TEXT, col_2 TEXT, col_3 TEXT);");
db.execSQL("INSERT INTO new_table SELECT _id, col_1, col_2, null FROM temp_table");
db.execSQL("DROP TABLE old_table");
db.execSQL("DROP TABLE temp_table");

Related

How to get PRIMARY KEY ID of objects in DataBase -android sqlite 3

I want to get a primary key of a saved object in a table in database I wrote a class to handle my database I want to add a function to it for getting the Id (I tried to give id to objects manually it didn't go well so I prefer the primary key id)so how should this function look like?and also if u see a thing that needs changing in my code please let me know.
public class DataBaseHandler extends SQLiteOpenHelper {
private static int _ID =0;
private int ID =0;
private ArrayList<marker_model> markerList=new ArrayList<>();
public DataBaseHandler(Context context) {
super(context, Constans.TABLE_NAME, null, Constans.DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "+Constans.TABLE_NAME+
" ("+Constans.MARKER_ID+" INTEGER PRIMARY KEY, "+
Constans.MARKER_TITLE+" TEXT, " +Constans.MARKER_DESCRIPTION+" TEXT ,"+Constans.My_MARKER_ID+" INT );");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS "+Constans.TABLE_NAME);
onCreate(db);
}
public void AddMarker(marker_model marker){
marker.set_Id(_ID);
SQLiteDatabase db=this.getWritableDatabase();
ContentValues values=new ContentValues();
values.put(Constans.MARKER_TITLE,marker.getTitle());
values.put(Constans.My_MARKER_ID,marker.get_Id());
values.put(Constans.MARKER_DESCRIPTION,marker.getDescription());
db.insert(Constans.TABLE_NAME,null,values);
db.close();
Log.d(TAG, "AddMarker: Successfully added to DB");
_ID++;
}
public ArrayList<marker_model> getMarkers(){
markerList.clear();
SQLiteDatabase db =getReadableDatabase();
Cursor cursor=db.query(Constans.TABLE_NAME
,new String[]{Constans.My_MARKER_ID,Constans.MARKER_TITLE,
Constans.MARKER_DESCRIPTION},null,null,null,null,null);
if (cursor.moveToFirst()){
do {
ID=0;
marker_model model=new marker_model();
model.set_Id(_ID);
model.setDescription(cursor.getString(cursor.getColumnIndex(Constans.MARKER_DESCRIPTION)));
model.setTitle(cursor.getString(cursor.getColumnIndex(Constans.MARKER_TITLE)));
markerList.add(model);
ID++;
}while(cursor.moveToNext());
}
cursor.close();
db.close();
return markerList;
}
public int getMarkerPrimaryId(Marker marker){
}
}
Assuming that you want to get the _id (the primary key) from the database and that marker is an instance of a marker_model object AND that
marker_model has methods getTitle and getDescription that return a string with the respective values, then something along the lines of the following would work.
public long getMarkerPrimaryId(Marker marker){
long rv = 0;
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[]{Constans.My_MARKER_ID};
String whereclause = Constans.MARKER_TITLE + "=?" +
Constans.MARKER_DESCRIPTION + "=?";
String[] whereargs = new String[]{
marker.getTitile,
marker.getDescription
}
Cursor cursor = db.query(Constans.TABLE_NAME,
columns,
whereclause,
whereargs,
null,null,null);
if (cursor.getCount() > 0) {
cursor.moveToFirst();
rv = cursor.getLong(cursor.getColumnIndex(Constans.My_MARKER_ID);
}
cursor.close;
db.close;
return rv;
}
However, if your issue is that getMarkers is not setting the Id member appropriately (i.e. to match the id in the database), then changing model.set_Id(_ID);
to
model.set_Id(cursor.getLong(cursor.getColumnIndex(Constans.My_MARKER_ID));
would suffice.
If your expectation is that an automatically generated incrementing _id is to be used the addMarker is a little flawed. Simply by removing the line values.put(Constans.My_MARKER_ID,marker.get_Id()); will result in _id being automatically generated (which how _id's tend to be used).
The following (BACKGROUND paragraph mostly) explains much about automatically generated unique identifiers (even though it is about AUTOINCREMENT you likely DO NOT want to code AUTOINCREMENT).
Id suggest that rather than :-
if (cursor.moveToFirst()){
do {
...
}while(cursor.moveToNext());
using :-
while (cursor.moveToNext() {
....
}
is simpler (a cursor, when created, will be positioned to before the first row (moveToPosition(-1) has the same effect) , moveToNext() will move to the first row the first time, if there are no rows the loop will not be entered (you may wish to consider this and the state of returned markerlist)).
Note! the above has been written without testing, so there may be the odd mistake.

upgrade sqlite database in my app

So, I already have my app on playstore....
Now, I want to add a column to the database in my app. For this, I must upgrade my databse which can be done by changing the database version.
The users will already have some stuff in the database and when I will upload the updated version of my app (with changed version of the databse), it will create a new databse and user will loose all the stuff he/she has in his/her database.
What is the solution for this issue? And how to backup / restore contents of the old databse to new database? (I know how to backup the database by simply copy pasting the database to external storage programatically).
You can use onUpgrade() method for handling this.
Something like this:
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 1 && newVersion == 2) {
db.execSQL("create temporary table people_tmp ("
+ "id integer, name text, position text, posid integer);");
db.execSQL("insert into people_tmp select id, name, position, posid from people;");
db.execSQL("drop table people;");
db.execSQL("create table people ("
+ "id integer primary key autoincrement,"
+ "name text, posid integer);");
db.execSQL("insert into people select id, name, posid from people_tmp;");
db.execSQL("drop table people_tmp;");
}
}
So. You are creating temporary table and saving all needed info inside that table. Next you dropping your table, creating new one and inserting values to it from your temporary table. You can add additional fields and feel free to put there all what you want.
UPDATE:
After a little googling i found an easier solution:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// If you need to add a column
if (newVersion == 2) {
db.execSQL("ALTER TABLE foo ADD COLUMN new_column INTEGER DEFAULT 0");
}
}
Alter table method will change your database structure without loosing data.
If you are only adding a new column, you can alter existing table instead of create new table. An example:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(oldVersion<2){
db.execSQL("ALTER TABLE "+this.getTableName()+" ADD COLUMN "+COLUMNS.NAME+ " integer default 0;", null);
db.execSQL("UPDATE "+this.getTableName()+ " SET "+COLUMNS.NAME+ "="+COLUMNS.NAMEVALUE+";", null);
}
};
Here is Android documentation on ALTER TABLE use case in onUpgrade(). So in this case, if you are not rename or remove existing table, you don't need to backup old table.
If you add new columns you can use ALTER TABLE to insert them into a
live table.
Also see: https://stackoverflow.com/a/8291718/2777098

android initialize database install once

Im developing an app for Android and Im looking for a way to create a database table and initialize it but to do it only once when the app is installed or when its first time started. After the table is created and initialized for the first time I do not want this code to rune any more because it inserts a lot of data in to the table.
Haw can I do that?
Extend SQLiteOpenHelper
public class MyDB extends SQLiteOpenHelper {
Context c;
public MyDB(Context c) {
super(c, DB_NAME, null, DB_VERSION);
this.c=c;
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table "+DB_TABLE+" ( _id integer primary key autoincrement, "+COL_1+" text, "+COL_2+" text, "+COL_3+" text, "+COL_4+" integer, "+COL_5+" integer, "+COL_6+" text );");
}
#Override
public void onUpgrade(SQLiteDatabase db, int from, int to) {
Log.i("onUpgrade", "From Version "+from+" to version "+to);
switch(from) {
case 0:
db.execSQL("DROP TABLE IF EXISTS wine;");
db.execSQL("create table "+DB_TABLE+" ( _id integer primary key autoincrement, "+COL_1+" text, "+COL_2+" text, "+COL_3+" text, "+COL_4+" integer, "+COL_5+" integer, "+COL_6+" text );");
case 1:
db.execSQL("ALTER TABLE "+DB_TABLE+" ADD COLUMN "+COL_6+" text;"); //comment was not in the original database
break;
}
}
You need to create a class that is extends SQLLiteHelper and overwrite the onCreate() method to create your table and insert any rows. After this time, the database queries will not be executed unless you get a writable instance of it and pass in your own queries.
If you wanted to make changes to your app in the future, you would need to override the onUpgrade() method to make changes to your database.
Check if database is empty on every start of your app, if so insert your data, otherwise ignore.

How to make dynamic column using Android SQLite Database

Im writing application on Android and im using SQlite database.
I want to be able to add columns to my table by the user choice.
so the user can add any column that he wants the to table. For example the user have "animal" table and he want to add column for "dog", "cat" and "fish".
I have read about some solutions and i didnt saw one that can help me.
I read that the simple way to add column is using:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// If you need to add a column
if (newVersion > oldVersion) {
db.execSQL("ALTER TABLE " + TableName + " ADD COLUMN " + ColumnName);
}
}
But my problem with this that i cant choose what is the name of the column that will be added to the table by the user choise, there is no parameter for string.
So i tried using something like this, and to call it directly.
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion, String newColumnName) {
// If you need to add a column
if (newVersion > oldVersion) {
db.execSQL("ALTER TABLE " + TableName + " ADD COLUMN " + newColumnName);
}
}
But i got error for this method.
I have another question about the database version.
The onUpgrade method get automaticly called when onCreate get called.
In onUpgrade there is oldVersion and newVersion parameters for the database version. when do i set the oldVersion and newVersion parameters? How i set my newVersion parameter to 2,3,4...?
You can create an auxiliary table to hold the extra column data. Queries to your primary table can be converted into queries on a new view.
create table if not exists animal (pk integer primary key, name);
create table if not exists animal_aux (animal_pk, col_name, col_val);
create view if not exists animal_view
as select animal.name as name,
ct.col_val as cat,
dt.col_val as dog
from animal, animal_aux as ct, animal_aux as dt
where animal.pk = ct.animal_pk
and animal.pk = dt.animal_pk
and ct.col_name = 'cat'
and dt.col_name = 'dog'
;
This schema should be enhanced to make animal_pk, col_name a primary key, or at least unique in animal_aux. You may also need triggers to add or remove entries in the aux table when you insert or delete entries in the animal table.
Example:
sqlite> select * from animal_view;
sqlite> insert into animal values (NULL, 'fred');
sqlite> select * from animal_view;
sqlite> select * from animal;
1|fred
sqlite> insert into animal_aux values (1, "dog", "beagle");
sqlite> insert into animal_aux values (1, "cat", "siamese");
sqlite> select * from animal_view;
fred|siamese|beagle
sqlite>
Each time you add a virtual column, you'll need to
drop view animal_view;
and then re-create it with the appropriate extra columns and where clauses.
final static String Database_name="empDb.db";
public DatabaseHelper(Context context) {
super(context, Database_name, null, 1);
}
#Override public void onCreate(SQLiteDatabase db) {
db.execSQL("create table emp_tbl (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,salary TEXT)");
}
#Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS tbl_emp");
}
blog: https://itmulc.blogspot.com/2016/08/android-sqlite-database-with-complete.html
Get more info about it.
https://www.youtube.com/watch?v=W8-Z85oPNmQ

onCreate not being called after getWritableDatabase/getReadableDatabase

My app's got a database with three tables in it: one to store the names of the people it tracks, one to track an ongoing event, and one - for lack of a better term - for settings.
I load the first table when the app starts. I ask for a readable database to load in members to display, and later I write to the database when the list changes. I've had no problems here.
The other two tables, however, I can't get to work. The code in the helper classes is identical with the exception of class names and column names, and (at least until the point where I try to access the table) the code to use the table is nearly identical as well.
Here's the code for my helper class (I've got a separate helper for each table, and as I said, it's identical except for class names and columns):
public class db_MembersOpenHelper extends SQLiteOpenHelper
{
public static final String TABLE_NAME = "members_table";
public static final String[] COLUMN_NAMES = new String[] {
Constants.KEY_ID,
"name",
"score"
};
private static final String TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + " ("
+ COLUMN_NAMES[0] + " INTEGER PRIMARY KEY autoincrement, "
+ COLUMN_NAMES[1] + " TEXT, "
+ COLUMN_NAMES[2] + " INTEGER);";
public db_MembersOpenHelper(Context context)
{
super(context, Constants.DATABASE_NAME, null, Constants.DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) { db.execSQL(TABLE_CREATE); }
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.w("TaskDBAdapter", "Upgrading from version " + oldVersion + " to " + newVersion + ".");
// Do nothing. We do not have any updated DB version
}
}
Here's how I use it successfully:
db_MembersOpenHelper membersDbHelper = new db_MembersOpenHelper(this);
SQLiteDatabase membersDb = membersDbHelper.getReadableDatabase();
Cursor membersResult = membersDb.query(TABLE_NAME, null, null, null, null, null, null);
members = new HashMap<String, Integer>();
membersResult.moveToFirst();
for(int r = 0; r < membersResult.getCount(); r++)
{
members.put(membersResult.getString(1), membersResult.getInt(2));
membersResult.moveToNext();
}
membersDb.close();
And here's where it fails:
db_PlayersOpenHelper playersDbHelper = new db_PlayersOpenHelper(this);
final SQLiteDatabase playersDb = playersDbHelper.getWritableDatabase();
if(newGame)
{
for(String name : players)
{
ContentValues row = new ContentValues();
row.put(COLUMN_NAMES[1], name);
row.put(COLUMN_NAMES[2], (Integer)null);
playersDb.insert(TABLE_NAME, null, row);
}
}
The first one works like a charm. The second results in ERROR/Database(6739): Error inserting achievement_id=null name=c
android.database.sqlite.SQLiteException: no such table: players_table: , while compiling: INSERT INTO players_table(achievement_id, name) VALUES(?, ?);
...
I did do some testing, and the onCreate method is not being called at all for the tables that aren't working. Which would explain why my phone thinks the table doesn't exist, but I don't know why the method isn't getting called.
I can't figure this out; what am I doing so wrong with the one table that I accidentally did right with the other?
I think the problem is that you are managing three tables with with three helpers, but only using one database. SQLiteOpenHelper manages on database, not one table. For example, it checks to see whether the database, not table, exists when it starts. It already does, so onCreate() does not fire.
I would manage all tables with one helper.
Let me see if I get this right. You are trying to create one database with three tables. But when you create the database, you create just one table; you are somehow instantiating the same database at a different place and wonder why its onCreate method doesn't get called. Is this a correct interpretation?
My strategy would be to try and create all three tables in the single onCreate() method.
If you are working with multiple tables, then you have to create all of the tables at once. If you have run your application first and later you update your database, then it will not upgrade your DB.
Now delete your application, then run it again.
There is one more solution but it is not proper. You can declare onOpen method in which you can call onCreate. And add IF NOT EXISTS before table name in your create table string. – Sourabh just now edit

Categories

Resources