I have an android app that needs to check if there's already a record in the database, and if not, process some things and eventually insert it, and simply read the data from the database if the data does exist. I'm using a subclass of SQLiteOpenHelper to create and get a rewritable instance of SQLiteDatabase, which I thought automatically took care of creating the table if it didn't already exist (since the code to do that is in the onCreate(...) method).
However, when the table does NOT yet exist, and the first method ran upon the SQLiteDatabase object I have is a call to query(...), my logcat shows an error of "I/Database(26434): sqlite returned: error code = 1, msg = no such table: appdata", and sure enough, the appdata table isn't being created.
Any ideas on why?
I'm looking for either a method to test if the table exists (because if it doesn't, the data's certainly not in it, and I don't need to read it until I write to it, which seems to create the table properly), or a way to make sure that it gets created, and is just empty, in time for that first call to query(...)
EDIT
This was posted after the two answers below:
I think I may have found the problem. I for some reason decided that a different SQLiteOpenHelper was supposed to be created for each table, even though both access the same database file. I think refactoring that code to only use one OpenHelper, and creating both tables inside it's onCreate may work better...
Try this one:
public boolean isTableExists(String tableName, boolean openDb) {
if(openDb) {
if(mDatabase == null || !mDatabase.isOpen()) {
mDatabase = getReadableDatabase();
}
if(!mDatabase.isReadOnly()) {
mDatabase.close();
mDatabase = getReadableDatabase();
}
}
String query = "select DISTINCT tbl_name from sqlite_master where tbl_name = '"+tableName+"'";
try (Cursor cursor = mDatabase.rawQuery(query, null)) {
if(cursor!=null) {
if(cursor.getCount()>0) {
return true;
}
}
return false;
}
}
I know nothing about the Android SQLite API, but if you're able to talk to it in SQL directly, you can do this:
create table if not exists mytable (col1 type, col2 type);
Which will ensure that the table is always created and not throw any errors if it already existed.
Although there are already a lot of good answers to this question, I came up with another solution that I think is more simple. Surround your query with a try block and the following catch:
catch (SQLiteException e){
if (e.getMessage().contains("no such table")){
Log.e(TAG, "Creating table " + TABLE_NAME + "because it doesn't exist!" );
// create table
// re-run query, etc.
}
}
It worked for me!
This is what I did:
/* open database, if doesn't exist, create it */
SQLiteDatabase mDatabase = openOrCreateDatabase("exampleDb.db", SQLiteDatabase.CREATE_IF_NECESSARY,null);
Cursor c = null;
boolean tableExists = false;
/* get cursor on it */
try
{
c = mDatabase.query("tbl_example", null,
null, null, null, null, null);
tableExists = true;
}
catch (Exception e) {
/* fail */
Log.d(TAG, tblNameIn+" doesn't exist :(((");
}
return tableExists;
Yep, turns out the theory in my edit was right: the problem that was causing the onCreate method not to run, was the fact that SQLiteOpenHelper objects should refer to databases, and not have a separate one for each table. Packing both tables into one SQLiteOpenHelper solved the problem.
// #param db, readable database from SQLiteOpenHelper
public boolean doesTableExist(SQLiteDatabase db, String tableName) {
Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '" + tableName + "'", null);
if (cursor != null) {
if (cursor.getCount() > 0) {
cursor.close();
return true;
}
cursor.close();
}
return false;
}
sqlite maintains sqlite_master table containing information of all tables and indexes in database.
So here we are simply running SELECT command on it, we'll get cursor having count 1 if table exists.
You mentioned that you've created an class that extends SQLiteOpenHelper and implemented the onCreate method. Are you making sure that you're performing all your database acquire calls with that class? You should only be getting SQLiteDatabase objects via the SQLiteOpenHelper#getWritableDatabase and getReadableDatabase otherwise the onCreate method will not be called when necessary. If you are doing that already check and see if th SQLiteOpenHelper#onUpgrade method is being called instead. If so, then the database version number was changed at some point in time but the table was never created properly when that happened.
As an aside, you can force the recreation of the database by making sure all connections to it are closed and calling Context#deleteDatabase and then using the SQLiteOpenHelper to give you a new db object.
Kotlin solution, based on what others wrote here:
fun isTableExists(database: SQLiteDatabase, tableName: String): Boolean {
database.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '$tableName'", null)?.use {
return it.count > 0
} ?: return false
}
public boolean isTableExists(String tableName) {
boolean isExist = false;
Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '" + tableName + "'", null);
if (cursor != null) {
if (cursor.getCount() > 0) {
isExist = true;
}
cursor.close();
}
return isExist;
}
no such table exists: error is coming because once you create database with one table after that whenever you create table in same database it gives this error.
To solve this error you must have to create new database and inside the onCreate() method you can create multiple table in same database.
Important condition is IF NOT EXISTS to check table is already exist or not in database
like...
String query = "CREATE TABLE IF NOT EXISTS " + TABLE_PLAYER_PHOTO + "("
+ KEY_PLAYER_ID + " TEXT,"
+ KEY_PLAYER_IMAGE + " TEXT)";
db.execSQL(query);
i faced that and deal with it by try catch as simple as that i do what i want in table if it not exist will cause error so catch it by exceptions and create it :)
SQLiteDatabase db=this.getWritableDatabase();
try{
db.execSQL("INSERT INTO o_vacations SELECT * FROM vacations");
db.execSQL("DELETE FROM vacations");
}catch (SQLiteException e){
db.execSQL("create table o_vacations (id integer primary key ,name text ,vacation text,date text,MONTH text)");
db.execSQL("INSERT INTO o_vacations SELECT * FROM vacations");
db.execSQL("DELETE FROM vacations");
}
.....
Toast t = Toast.makeText(context, "try... " , Toast.LENGTH_SHORT);
t.show();
Cursor callInitCheck = db.rawQuery("select count(*) from call", null);
Toast t2a = Toast.makeText(context, "count rows " + callInitCheck.getCount() , Toast.LENGTH_SHORT);
t2a.show();
callInitCheck.moveToNext();
if( Integer.parseInt( callInitCheck.getString(0)) == 0) // if no rows then do
{
// if empty then insert into call
.....
Related
I have done a lot of research and was unable to find a suitable method to delete all the tables in an SQLite database. Finally, I did a code to get all table names from the database and I tried to delete the tables using the retrieved table names one by one. It didn't work as well.
Please suggest me a method to delete all tables from the database.
This is the code that I used:
public void deleteall(){
SQLiteDatabase db = this.getWritableDatabase();
Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);
do
{
db.delete(c.getString(0),null,null);
}while (c.moveToNext());
}
function deleteall() is called on button click whos code is given as below:
public void ButtonClick(View view)
{
String Button_text;
Button_text = ((Button) view).getText().toString();
if(Button_text.equals("Delete Database"))
{
DatabaseHelper a = new DatabaseHelper(this);
a.deleteall();
Toast.makeText(getApplicationContext(), "Database Deleted Succesfully!", Toast.LENGTH_SHORT).show();
}}
Use DROP TABLE:
// query to obtain the names of all tables in your database
Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);
List<String> tables = new ArrayList<>();
// iterate over the result set, adding every table name to a list
while (c.moveToNext()) {
tables.add(c.getString(0));
}
// call DROP TABLE on every table name
for (String table : tables) {
String dropQuery = "DROP TABLE IF EXISTS " + table;
db.execSQL(dropQuery);
}
Tim Biegeleisen's answer almost worked for me, but because I used AUTOINCREMENT primary keys in my tables, there was a table called sqlite_sequence. SQLite would crash when the routine tried to drop that table. I couldn't catch the exception either. Looking at https://www.sqlite.org/fileformat.html#internal_schema_objects, I learned that there could be several of these internal schema tables that I shouldn't drop. The documentation says that any of these tables have names beginning with sqlite_ so I wrote this method
private void dropAllUserTables(SQLiteDatabase db) {
Cursor cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);
//noinspection TryFinallyCanBeTryWithResources not available with API < 19
try {
List<String> tables = new ArrayList<>(cursor.getCount());
while (cursor.moveToNext()) {
tables.add(cursor.getString(0));
}
for (String table : tables) {
if (table.startsWith("sqlite_")) {
continue;
}
db.execSQL("DROP TABLE IF EXISTS " + table);
Log.v(LOG_TAG, "Dropped table " + table);
}
} finally {
cursor.close();
}
}
delete database instead of deleting tables and then create new with same name if you need. use following code
context.deleteDatabase(DATABASE_NAME);
or
context.deleteDatabase(path);
For me, the working solution is:
Cursor c = db.rawQuery(
"SELECT name FROM sqlite_master WHERE type IS 'table'" +
" AND name NOT IN ('sqlite_master', 'sqlite_sequence')",
null
);
if(c.moveToFirst()){
do{
db.execSQL("DROP TABLE " + c.getString(c.getColumnIndex("name")));
}while(c.moveToNext());
}
I have some trouble with a SQLite database with 1 table and 2 columns, column_id and word. I extended SQLiteAssetHelper as MyDatabase and made a constructor:
public MyDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
I need to check whether some string is in the database (in column word). I tried to modify the code from answer provided by Benjamin and dipali, but I used SQLiteAssetHelper and I can't get it to work. The method that I have in mind receives the string to search for as a parameter and returns a boolean if string is in the database.
public boolean someMethod(String s)
In addition, I tried to put the check on a background thread with AsyncTask because I have 60 strings to check.
TABLE_NAME and COLUMN_WORD should be self-explanatory.
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[] {COLUMN_WORD};
String where = COLUMN_WORD + " = ?";
String[] whereArgs = new String[] {s};
// select column_word from table where column_word = 's' limit 1;
Cursor cursor = db.query(TABLE_NAME, columns, where, whereArgs, null, null, null, "1");
if (cursor.moveToFirst()) {
return true; // a row was found
}
return false; // no row was found
}
You can do this in the background, but I don't think for a query like this it's even necessary.
EDIT
There are some improvements that should be made to the above for the sake of correctness. For one thing, the Cursor should be closed since it is no longer being used. A try-finally block will ensure this:
Cursor cursor = db.query(...);
try {
return cursor.moveToFirst();
} finally {
cursor.close();
}
However, this method doesn't need to obtain a whole `Cursor. You can write it as follows and it should be more performant:
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String sql = "select count(*) from " + TABLE_NAME + " where "
+ COLUMN_WORD + " = " + DatabaseUtils.sqlEscapeString(s);
SQLiteStatement statement = db.compileStatement(sql);
try {
return statement.simpleQueryForLong() > 0;
} finally {
statement.close();
}
}
You could add a catch block and return false if you think it's possible (and valid) to encounter certain exceptions like SQLiteDoneException. Also note the use of DatabaseUtils.sqlEscapeString() because s is now concatenated directly into the query string and thus we should be wary of SQL injection. (If you can guarantee that s is not malicious by the time it gets passed in as the method argument, then you could theoretically skip this, but I wouldn't.)
because of possible data leaks best solution via cursor:
Cursor cursor = null;
try {
cursor = .... some query (raw or not your choice)
return cursor.moveToNext();
} finally {
if (cursor != null) {
cursor.close();
}
}
1) From API KITKAT u can use resources try()
try (cursor = ...some query)
2) if u query against VARCHAR TYPE use '...' eg. COLUMN_NAME='string_to_search'
3) dont use moveToFirst() is used when you need to start iterating from beggining
4) avoid getCount() is expensive - it iterates over many records to count them. It doesn't return a stored variable. There may be some caching on a second call, but the first call doesn't know the answer until it is counted.
I'm trying to check to see if a table exists in and SQLite Database with Android using this in my SQLiteOpenHelper file
// Check to see if a table exists
public boolean isTableExists(String tblName) {
String existQuery = "SELECT name FROM sqlite_master WHERE name ='" + tblName + "' and type='table'";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(existQuery, null);
if(cursor != null){
cursor.close();
return true;
}
return false;
}
The call from my Activity is
if(db.isTableExists("characters") == false){
Intent p = new Intent("com.tml.rpgtodo.CREATECHARACTER");
startActivity(p);
}
I keep getting a NullPointerException on the row with the if statement. What am I doing wrong?
It sounds like db is null when you call if(db.isTableExists("characters") == false).
I also see that you initialize db inside of "isTableExists()". Is that a different "db"?
Sorry if this seems obvious. I'm trying to write a method to delete a row from a String showId. What would be the best way, and can Cursors only be used for Selects or also for Deletes and Updates?
These are the two methods I'm at so far:
public int deleteShowById1(String showId){
Cursor cursor = db.rawQuery("DELETE FROM tblShows WHERE showId = '" + showId+"'", null);
if (cursor.moveToFirst()) {
return 1;
} else
return -1;
}
public int deleteShowById2(String showId) {
String table_name = "tblShows";
String where = "showId='"+showId+"'";
return db.delete(table_name, where, null);
}
As we know from mysql query, it is same here in android.
String query = "DELETE FROM " +TABLE_NAME+ " WHERE " + COLUM_NAME+ " = " + "'"+VALUE +"'" ;
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL(query);
db.close();
VALUE may or may not have single quotation depending on datatype.
I tend to use the second method (db.delete), as I think using rawQuery is frowned upon.
If you do a select, then loop through the cursor to do updates or deletes, that would make sense, but to pass a cursor to do the delete or update doesn't make sense to me, as the program won't know how to parse the cursor results to get the correct fields.
I am doing my first app with a database and I am having a little trouble understanding the onUpgrade function. My database has a table with an items and a favorite column so that the user can favorite an item. Most implementations I see simply drop the table and reconstruct it but I don't want to do this. I want to be able to add more items to the table.
When the app is upgraded through the android marketplace does the database know its version number? So could I increment the version number in the code and then export it to the marketplace and when the user boots up the upgraded version for the first time then onUpgrade will be called?
If this is the case my onUpgrade would simply pull from a file and add the database items in. Is this a standard way of doing things or is there a better way of handling this in Android. I am trying to stay as standard as possible.
Thanks
Ok, before you run into bigger problems you should know that SQLite is limited on the ALTER TABLE command, it allows add and rename only no remove/drop which is done with recreation of the table.
You should always have the new table creation query at hand, and use that for upgrade and transfer any existing data. Note: that the onUpgrade methods runs one for your sqlite helper object and you need to handle all the tables in it.
So what is recommended onUpgrade:
beginTransaction
run a table creation with if not exists (we are doing an upgrade, so the table might not exists yet, it will fail alter and drop)
put in a list the existing columns List<String> columns = DBUtils.GetColumns(db, TableName);
backup table (ALTER table " + TableName + " RENAME TO 'temp_" + TableName)
create new table (the newest table creation schema)
get the intersection with the new columns, this time columns taken from the upgraded table (columns.retainAll(DBUtils.GetColumns(db, TableName));)
restore data (String cols = StringUtils.join(columns, ",");
db.execSQL(String.format(
"INSERT INTO %s (%s) SELECT %s from temp_%s",
TableName, cols, cols, TableName));
)
remove backup table (DROP table 'temp_" + TableName)
setTransactionSuccessful
(This doesn't handle table downgrade, if you rename a column, you don't get the existing data transfered as the column names do not match).
.
public static List<String> GetColumns(SQLiteDatabase db, String tableName) {
List<String> ar = null;
Cursor c = null;
try {
c = db.rawQuery("select * from " + tableName + " limit 1", null);
if (c != null) {
ar = new ArrayList<String>(Arrays.asList(c.getColumnNames()));
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (c != null)
c.close();
}
return ar;
}
public static String join(List<String> list, String delim) {
StringBuilder buf = new StringBuilder();
int num = list.size();
for (int i = 0; i < num; i++) {
if (i != 0)
buf.append(delim);
buf.append((String) list.get(i));
}
return buf.toString();
}
Next to Pentium10's excellent answer, here are some good examples from living code:
Android AOSP: com.android.providers.calendar.CalendarDatabaseHelper.java
Android AOSP: com.android.browser.BrowserProvider.java
OpenIntents Notepad: org.openintents.notepad.NotePadProvider.java
Thank you for clarifying that onUpgrade() will not support Remove/Drop statements #Pentium 10
For those of you who would like to know the exact moment when onUpgrade() gets called, it is during a call to either getReadableDatabase() or getWriteableDatabase().
To those who are not clear how it ensure it gets triggered...the answer is: It is triggered when the database version provided to the constructor of SqLiteOpenHelper is updated. Here is a example
public class dbSchemaHelper extends SQLiteOpenHelper {
private String sql;
private final String D_TAG = "FundExpense";
//update this to get onUpgrade() method of sqliteopenhelper class called
static final int DB_VERSION = 2;
static final String DB_NAME = "fundExpenseManager";
public dbSchemaHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
// TODO Auto-generated constructor stub
}
now to...onUpgrade()
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
sql = "ALTER TABLE " + fundExpenseSchema.Expense.TABLE_NAME + " ADD COLUMN " + fundExpenseSchema.Expense.FUNDID + " INTEGER";
arg0.execSQL(sql);
}
I've been using the solution proposed by #Pentium10 for a long time but today i had a problem, after doing alter table, getColumns from the original table still returns the same columns (in the new version of the db the table suffer mayor structure changes, some columns added some others), really i don't know why select statement does not reflect the structure changes, more over before creating my table again, select statement still returns the columns! When the table is not re-created yet!
So i manage solving this issue updating getColumns method using pragma table_info, like this:
/**
* Get a list of column base_dictionary for the selected table
*
* #param db
* Database that contains the table
* #param tableName
* Table name to be used
* #return A List of column name
*/
public static List<String> getColumns(SQLiteDatabase db, String tableName) {
List<String> ar = null;
Cursor c = null;
try {
c = db.rawQuery("pragma table_info(" + tableName + ")", null);
ar = new ArrayList<String>();
if (c != null && c.moveToFirst()) {
do {
ar.add(c.getString(c.getColumnIndexOrThrow("name")));
} while (c.moveToNext());
c.close();
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (c != null) c.close();
}
return ar;
}