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
.....
I am shipping the database file with our android application. For that I followed How to ship an Android application with a database? answered by Danny Remington - OMS.
Now I have to provide the new release In that the database schema has changed, so First I am copying the new db file from assets to data/data directory and writing a insert statement as following in the onUpgrade method of SqliteOpenHelper class:
//rename the old database
new File(DB_PATH + DATABASE_NAME).renameTo(new File(DB_PATH + "DB_old"));
try
{
//copting new db file from assets to data/data dir
copyDataBase();
}
catch (IOException e)
{
e.printStackTrace();
}
SQLiteDatabase DB_old = SQLiteDatabase.openDatabase(DB_PATH + "DB_old", null, SQLiteDatabase.OPEN_READWRITE);
SQLiteDatabase DB_new = SQLiteDatabase.openDatabase(DB_PATH + "DB_new", null, SQLiteDatabase.OPEN_READWRITE);
//insert statement from old to new db
String query = "INSERT INTO DB_new.table1(user_id,user_name) SELECT * FROM DB_old.table1";
DB_new.execSQL(query);
DB_old.close();
DB_new.close();
//deleting old db file
new File(DB_PATH + "DB_old").delete();
But the insert statement is throwing a exception android.database.sqlite.SQLiteException: no such table: DB_new.table1
How I can achieve this.Please help me on this.
In your sqlite helper, there is a method onUpgrade which triggers when the version you pass to the super's constructor (of the SqliteOpenHelper) changes. Thus, you need to change this number, so that onUpgrade fires, then put your code for "re-installing" the database inside this method.
Currently I'm using ContentProvider in my application. Because of "layers" and no actual need for provider - I'm working on optimizing data access as much as possible. Here is my attempt to do this:
public static String getPreferenceString(Context context, String key)
{
DatabaseHelper helper = new DatabaseHelper(context);
SQLiteDatabase database = helper.getReadableDatabase();
SQLiteStatement statement = database.compileStatement("SELECT Value FROM Preferences WHERE Key='" + key + "' LIMIT 1");
try
{
return statement.simpleQueryForString();
}
catch (Exception ex)
{
return "";
}
finally
{
statement.close();
database.close();
helper.close();
}
}
public static void setPreferenceString(Context context, String key, String value)
{
DatabaseHelper helper = new DatabaseHelper(context);
SQLiteDatabase database = helper.getReadableDatabase();
SQLiteStatement statement = database.compileStatement("INSERT OR REPLACE INTO Preferences (Key, UpdatedOn, Value) VALUES ('" +
key + "', '" +
Utility.getDateConvertedToUTCDBString(new Date()) + "', '" +
value + "'); ");
try
{
statement.execute();
}
finally
{
statement.close();
database.close();
helper.close();
}
}
Is that about as close as I can get to direct calls to SQLite?
Should I have all this .close() statements in my code?
In setPreferenceString I did copy/paste and called getReadableDatabase even though I write data and it works. Why?
Is that about as close as I can get to direct calls to SQLite?
AFAIK SQL queries are closest you can go against RDBs
Should I have all this .close() statements in my code?
Personally, I would not create a DatabaseHelper, an SQLiteDatabase, and an SQLiteStatement each time I call that method. I would create all this just before you need them, and close them when no needed anymore. Also centralizing this is a good idea IMHO (using a singleton, for example).
Also your SQL statement could be written like
SELECT Value FROM Preferences WHERE Key= ? LIMIT 1
This way you only have to prepare it once and bind parameters as you need the statement. Same goes for any SQL query.
I am creating a database and I'm inserting a row like so:
/* Add two DataSets to the Table. */
myDB.execSQL("INSERT INTO "
+ MY_DATABASE_TABLE
+ " (LastName, FirstName, Country, Age)"
+ " VALUES ('Gramlich', 'Nicolas', 'Germany', 20);");
myDB.execSQL("INSERT INTO "
+ MY_DATABASE_TABLE
+ " (LastName, FirstName, Country, Age)"
+ " VALUES ('Doe', 'John', 'US', 34);");
I have two questions regarding the above code:
How do I disallow duplicate records? I want to check incoming data at the insert time. How can I achieve that? Should I use IF NOT EXISTS?
How can I check whether that database is available or not? So far, without any success, I've tried:
private static String DB_PATH = "/data/data/YOUR_PACKAGE/databases/myDBName";
private static String DB_NAME = "myDBName";
private boolean checkDataBase() {
SQLiteDatabase checkDB = null;
try {
checkDB = SQLiteDatabase.openDatabase(DB_PATH, null,
SQLiteDatabase.OPEN_READONLY);
checkDB.close();
} catch (SQLiteException e) {
// database doesn't exist yet.
}
return checkDB != null ? true : false;
}
Any suggestions on how to achieve this?
to prevent duplicates create a UNIQUE INDEX on that table.
At runtime, you have at least two options:
let the index give you an exception if you attempt a duplicate, then catch that exception and do something else, or
Query the table pro-actively to see if the new record exists, and if you find it you know there is a duplicate, so do something else.
Here are a few suggestions:
When creating your tables, define the columns that you want to prevent duplicates as UNIQUE.
Your checkDataBase() method is a bit strange. Have you considered using the SQLiteOpenHelper class in your implementation to simplify things?
http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html
I'm extending the SQLiteOpenHelper class to help me connect and do my database work. According to the documentation, the OnCreate method should only be called if the database has not been created. Yet, my problem is that I am getting this error when I try to execute a query to insert a record.
ERROR/Database(214): Failure 1 (table Sample already exists) on 0x218688 when preparing
'CREATE TABLE Sample (RecId INT, SampleDesc TEXT);'.
The only place this Create query is used in code is the OnCreate method which looks like this.
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SAMPLE_TABLE_CREATE);
}
Note: I'm following a tutorial - the only thing I've done different is make the SQLiteDatabase object public instead of private so that I could extend this class for each entity, letting the public SQLiteDatabase object from the inherited DataHelper class do all the work
Here is the method that makes the call that fails.
//This method is in the class that extends DataHelper (See note on tutorial)
public void createSample(Sample sample)//next action form
{
String id = sample.getId();
String name = sample.getSummary();
String query = "INSERT INTO " + SAMPLE_TABLE_NAME + "( " + SAMPLE_Id + "," +
SAMPLE_NAME + ") " + " VALUES (" + id + "," + name + ")";
try{
data.rawQuery(query, null);
}
catch(SQLException e){
Log.i("Sample", "Errors: Sample LN60: " + e.getMessage());
}
}
Can someone tell me what I'm doing wrong? Or maybe a hack (i.e. check if table exists before executing create statement)
Please let me know what other code I can post to solve this...
Is it due to you've execute it your activity once and never destroy the DB after that?
And 2nd run you'd hit this error.
Database is stored in /data/data/YOUR_PACKAGE/databases/, so a workaround would be to check if the DB exists here before creating it.
//The Android's default system path of your application database.
private static String DB_PATH = "/data/data/YOUR_PACKAGE/databases/";
private static String DB_NAME = "myDBName";
SQLiteDatabase checkDB = null;
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
if(checkDB){
//do nothing
}else{
//create DB
}
Code source here
The first error is quite simply because you are creating a table that already exists, so yes adding a check if the table exists prior to creating it would be good. Once an SQLite dB is created or made it will stay until someone or something deletes it, unlike the default onCreate() call which resembles re-creating or drawing your screen.
every time you call getWritableDatabase() onCreate() method is called.