I have read most of the questions related to this exception but none of them are clear or indicative of why db.insert would throw this error. It was working fine without errors until I manually deleted the db from DDMS. Following is my SQLiteOpenHelper code:
public class LoginSQLiteOpenHelper extends SQLiteOpenHelper {
public static final String DB_NAME = "logincredentials.sqlite";
public static final int DB_VERSION_NUMBER = 1;
public static final String DB_TABLE_NAME = "credentials";
public static final String USERNAME = "user_name";
public static final String PASSWORD = "password";
private static final String DB_CREATE_SCRIPT = "create table " + DB_TABLE_NAME +
"( _id integer primary key autoincrement," +
USERNAME + " text not null, " +
PASSWORD + " text not null );" ;
public LoginSQLiteOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION_NUMBER);
}
#Override
public void onCreate(SQLiteDatabase aSqliteDB) {
Logger.d("Create", "Creating the database...");
aSqliteDB.execSQL(DB_CREATE_SCRIPT);
}
}
My code for inserting the values is:
ContentValues contentValues = new ContentValues();
contentValues.put(LoginSQLiteOpenHelper.USERNAME, loginId);
contentValues.put(LoginSQLiteOpenHelper.PASSWORD, password);
database.insert(LoginSQLiteOpenHelper.DB_TABLE_NAME, null, contentValues);
This is why it occurred to me. If you declare one of your column name type as UNIQUE in your Create Table query in Database and try to insert a non unique variable, it invokes SQLiteConstraintException error.
A UNIQUE constraint is similar to a PRIMARY KEY constraint, except that a single table may have any number of UNIQUE constraints. For each UNIQUE constraint on the table, each row must feature a unique combination of values in the columns identified by the UNIQUE constraint. As with PRIMARY KEY constraints, for the purposes of UNIQUE constraints NULL values are considered distinct from all other values (including other NULLs). If an INSERT or UPDATE statement attempts to modify the table content so that two or more rows feature identical values in a set of columns that are subject to a UNIQUE constraint, it is a constraint violation. Source - http://www.sqlite.org/lang_createtable.html
I have read pretty much all forums looking for an exact reason for the occurrence of this exception. However, nowhere it clearly states so. However, by means of this code of mine, I can explain why it ocurred for me.
The code snippet I provided, is actually flawless. I am doing exactly what is required to do a db.insert().
However, i figured out the exception in 2 steps.
1. first time when i inserted values, I did not insert a value for the column Password.
2. second time, I added a value for column for Password, but due to incorrect passing of values it was null.
hence, I deduced from this exercise, that no column are allowed null values. You must initialize them with some value.
Please feel free to comment/add or correct me if I am wrong. I would like anyone else running into this issue to be clear on it as there are no good documentation on this exception.
Related
Hi in my senario there is a table with 4 columns and im trying to create another table with connection to the first table but i dont whay im getting this error in logcat
2019-10-28 01:04:00.853 29812-29812/com.test.fastfoodfinder E/SQLiteDatabase: Error inserting notes_main=testeststststststststs
android.database.sqlite.SQLiteException: no such table: notes (code 1 SQLITE_ERROR): , while compiling: INSERT INTO notes(notes_main) VALUES (?)
so i have created a class for my data base and this is what i have done
public class RestaurantDBHelper extends SQLiteOpenHelper {
private final static String DATABASE_NAME = "FastFood_DataBase.db";
private final static int DATABASE_VERSION = 1;
private final static String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
"(" + COLUMN_RESTAURANT_ID + " INTEGER PRIMARY KEY ," +
COLUMN_RESTAURANT_NAME + " TEXT, " +
COLUMN_RESTAURANT_ADDRESS + " TEXT, " +
COLUMN_RESTAURANT_TYPE + " INTEGER, " +
COLUMN_RESTAURANT_IMAGE + " INTEGER);";
private final static String CREATE_TABLE_NOTES = "CREATE TABLE " + TABLE_NAME_NOTES +
"(" + COLUMN_NOTES_ID + " INTEGER PRIMARY KEY, "
+ COLUMN_NOTES + " TEXT," + "FOREIGN KEY (" + COLUMN_NOTES_ID + ") REFERENCES " + TABLE_NAME +"(restaurant_id) ON DELETE CASCADE)";
public final static String DELETE_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME;
public RestaurantDBHelper(#Nullable Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("PRAGMA FOREIGN_KEYS = ON;");
db.execSQL(CREATE_TABLE);
db.execSQL(CREATE_TABLE_NOTES);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(DELETE_TABLE);
onCreate(db);
}
public void addRestaurant(Restaurant restaurant) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_RESTAURANT_NAME, restaurant.getName());
values.put(COLUMN_RESTAURANT_ADDRESS, restaurant.getAddress());
values.put(COLUMN_RESTAURANT_TYPE,restaurant.getType());
values.put(COLUMN_RESTAURANT_IMAGE, restaurant.getType());
db.insert(TABLE_NAME, null, values);
db.close();
}
public void addNotes (Restaurant restaurant) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_NOTES,restaurant.getNote());
db.insert(TABLE_NAME_NOTES,null,values);
db.close();
}
and
public class RestaurantContract {
public static class EntryRestaurants {
public final static String TABLE_NAME = "restaurants";
public final static String COLUMN_RESTAURANT_ID = "restaurant_id";
public final static String COLUMN_RESTAURANT_NAME = "restaurant_name";
public final static String COLUMN_RESTAURANT_ADDRESS = "restaurant_address";
public final static String COLUMN_RESTAURANT_TYPE = "restaurant_type";
public final static String COLUMN_RESTAURANT_IMAGE = "restaurant_image_type";
public final static String COLUMN_RESTAURANT_NOTE_ID = "note_id";
public final static String TABLE_NAME_NOTES = "notes";
public final static String COLUMN_NOTES_ID = "notes_id";
public final static String COLUMN_NOTES = "notes_main";
public final static int RESTAURANT_TYPE_DELIVERY = 1;
public final static int RESTAURANT_TYPE_SITDOWN = 2;
public final static int RESTAURANT_TYPE_TAKEAWAY = 3;
}
}
im kind a new in android so any help would be appreciated,thanks
I believe that your issue is with the onCreate method. This ONLY runs when the database is created, it does not run every time the App is run.
The easiest solution, assuming that you do not need to keep any existing data, is to either delete the App's data or to uninstall the App. After doing either rerun the App and the new table will be created as the onCreate method will then run.
Furthermore it is no use turning FOREIGN KEYS on in the onCreate method. FOREIGN KEYS need to be turned on every time the App is run. To fix this, override the onConfigure method and then use db.setForeignKeyConstraintsEnabled(true);
this is just a convenient alternative to using db.execSQL("PRAGMA FOREIGN_KEYS = ON;");, so if you prefer you could use this when overriding the onConfigure method.
e.g. add this method to the RestaurantDBHelper class :-
#Override
public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
db.setForeignKeyConstraintsEnabled(true);
}
However, you will then have issues when trying to add notes as the child will be set to null and thus their will not be a link/map/association/reference between the added note and the restaurant.
You need to use something like :-
public long addNote(String note, long restaurantId) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_NOTES,note);
values.put(COLUMN_NOTES_ID,restaurantId);
return db.insert(TABLE_NAME_NOTES,null,values);
}
BUT then you may then have an issue as to determining the id of the restaurant.
BUT then you may then encounter a further issue in that you could only have one note per restaurant as the column used to reference the restaurant is defined as INTEGER PRIMARY KEY and is therefore is a UNIQUE column (the same value can only be used once (an exception is null as a null is considered to be unique to another null)).
If the requirement is for one note per restaurant then there is no need for the separate table the relationship is a one to one relationship so the value can be stored in the restaurant table.
If you want a restaurant to have multiple notes (one to many relationship) then you should not make the column INTEGER PRIMARY KEY, INTEGER would suffice. Then a number of notes could reference the same restaurant.
If you wanted a note to be able to be be applied to a number of restaurants then you'd use a third mapping/line/reference/associative table (other name probably also exist). Such a table would have two columns one to reference the restaurant and the other to reference the note. You would then have a many to many relationship between restaurants and notes (a note could be used by many restaurants and a restaurant could use many notes).
You may find The 3 Types of Relationships in Database Design helpful.
You have enabled foreign key constraints and thus must have a unique primary key for the foreign key to reference.
https://sqlite.org/foreignkeys.html#fk_indexes
says
If the database schema contains foreign key errors that require looking at more than one table definition to identify, then those errors are not detected when the tables are created. Instead, such errors prevent the application from preparing SQL statements that modify the content of the child or parent tables in ways that use the foreign keys. Errors reported when content is changed are "DML errors" and errors reported when the schema is changed are "DDL errors". So, in other words, misconfigured foreign key constraints that require looking at both the child and parent are DML errors. The English language error message for foreign key DML errors is usually "foreign key mismatch" but can also be "no such table" if the parent table does not exist. Foreign key DML errors are reported if:
The parent table does not exist, or
The parent key columns named in the foreign key constraint do not exist, or
The parent key columns named in the foreign key constraint are not the primary key of the parent table and are not subject to a unique constraint using collating sequence specified in the CREATE TABLE, or
The child table references the primary key of the parent without specifying the primary key columns and the number of primary key columns in the parent do not match the number of child key columns.
Given that you never give the restaurant_id a value when you inserting a restaurant then the primary key of the restaurants is probably always null and thus not unique
(Yes according to https://www.sqlitetutorial.net/sqlite-primary-key/ to make the current version of SQLite compatible with the earlier version, SQLite allows the primary key column to contain NULL values. )
So I would say the solution is to create restaurant entries with a unique primary key value when you insert a restaurant or get the database to generate a unique value creating the the restaurant table with the line:-
private final static String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
"(" + COLUMN_RESTAURANT_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
....
One way to confirm this is download FastFood_DataBase.* files using Device Explorer in your app's database directory and then open it up in https://sqlitebrowser.org/ on your computer to confirm the contents.
I have got the error message " or expected, got 'Index'" when I was trying to create a table and I do not really understand why is the code expecting a column definition or table constraint at this line
I have tried with changing the whitespaces, however that only change the place where the error is prompted. The content of the error message does not change
This is the part that I have declared the strings
public class TaskEntry implements BaseColumns {
public static final String TABLE = "Users";
public static final String INDEX = "Index";
public static final String COL_TASK_TITLE = "title";
}
The following is my code for the creating table part
public void onCreate(SQLiteDatabase db) {
String createTable = "CREATE TABLE " + Item_contract.TaskEntry.TABLE + " ( " +
Item_contract.TaskEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
Item_contract.TaskEntry.INDEX + " INTEGER NOT NULL, " +
Item_contract.TaskEntry.COL_TASK_TITLE + " TEXT NOT NULL" + ");";
db.execSQL(createTable);
}
You cannot use INDEX as a column name as it is a keyword.
The SQL standard specifies a large number of keywords which may not be
used as the names of tables, indices, columns, databases, user-defined
functions, collations, virtual table modules, or any other named
object. The list of keywords is so long that few people can remember
them all. For most SQL code, your safest bet is to never use any
English language word as the name of a user-defined object.
SQL As Understood By SQLite - SQLite Keywords
So change
public static final String INDEX = "Index";
perhaps to
public static final String INDEX = "IX";
You could enclose the column name if you really wanted it to be INDEX e.g.
public static final String INDEX = "[Index]";
As per :-
If you want to use a keyword as a name, you need to quote it. There are four ways of quoting keywords in SQLite:
'keyword' A keyword in single quotes is a string literal.
"keyword" A keyword in double-quotes is an identifier.
[keyword] A keyword enclosed in square brackets is an identifier. This is not standard SQL. This quoting mechanism is used by MS Access and SQL Server and is included in SQLite for compatibility.
`keyword` A keyword enclosed in grave accents (ASCII code 96) is an identifier. This is not standard SQL. This quoting mechanism is used by MySQL and is included in SQLite for compatibility.
SQL As Understood By SQLite - SQLite Keywords
Note
You will have to do one of the following to get the onCreate method to run and thus alter the schema:-
Delete the App's data.
Uninstall the App.
I'm trying to make a new table for my database with sqlite:
String CREATE_ARCHIVE_TABLE =
"CREATE TABLE {0} ({1} INTEGER PRIMARY KEY AUTOINCREMENT," +
" {2} TEXT NOT NULL, {3} TEXT NOT NULL, {4} TEXT NOT NULL, {5} INTEGER);";
db.execSQL(MessageFormat.format(CREATE_ARCHIVE_TABLE,AItext.TABLE_NAME,AItext._ID,
AItext.TITLE,AItext.MESSAGE,AItext.DATE,AItext.TYPE));
with the interface:
public interface AItext extends BaseColumns {
String TABLE_NAME = "table_name";
String TITLE = "title";
String MESSAGE = "message";
String DATE = "date";
String TYPE = "type";
String[] COLUMNS = new String[]
{ _ID, TITLE, MESSAGE, DATE, TYPE };
}
but I have the following exception and I can't see the error
android.database.sqlite.SQLiteException: near "INTEGER": syntax error: ,
while compiling: CREATE TABLE archive_contacts_name (_id INTEGER PRIMARY KEY
AUTOINCREMENT, message INTEGER, name TEXT NOT NULL, phone TEXT NOT NULL,
check INTEGER, note TEXT NOT NULL);
As #aim has said, CHECK is in fact a SQLite Keyword that cannot be used as a column name.
A CHECK constraint may be attached to a column definition or specified as a table constraint. In practice it makes no difference. Each time a new row is inserted into the table or an existing row is updated, the expression associated with each CHECK constraint is evaluated and cast to a NUMERIC value in the same way as a CAST expression. If the result is zero (integer value 0 or real value 0.0), then a constraint violation has occurred. If the CHECK expression evaluates to NULL, or any other non-zero value, it is not a constraint violation. The expression of a CHECK constraint may not contain a subquery.
CHECK constraints have been supported since version 3.3.0. Prior to version 3.3.0, CHECK constraints were parsed but not enforced.
Don't immediately flag me for a duplicate question. My issue is different because I have a correctly formatted SQL query.
public static final String TABLE_NAME = "log";
public static final String COLUMN_ID = "_id";
public static final String LOG_TEXT = "logtext";
private static final String TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + " (" +
COLUMN_ID + " integer primary key autoincrement, " +
LOG_TEXT + " TEXT not null);";
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TABLE_CREATE);
}
and I query here
String[] columns = {LOG_TEXT,COLUMN_ID};
Cursor cursor = helper.getReadableDatabase().query(TABLE_NAME, columns, null, null, null, null, COLUMN_ID + " desc");
and I catch this the exception generated containing the sql query.
catch(Exception e){
Log.D("sql Exception",e.getMessage());}
and it returns
no such column: _id: , while compiling: SELECT logtext, _id FROM log ORDER BY _id desc
I'm familar with Oracle SQL and relational databases in general. Is it my ORDER BY clause? I was certain you can ALWAYS use order by. It doesn't have the same behavior as GROUP BY.
Any ideas on why the exception?
Incase anyone wants to see i'm updating with my ArrayAdaptor statements. I'm using the cursor in a listview
String[] data = query();
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, data);
listView.setAdapter(adapter);}
Rewrite
Whenever you change the schema in TABLE_CREATE you must inform you app of these changes, they will not happen automatically when you change TABLE_CREATE. The easiest way to do this is to increment your database_version in your extended SQLiteOpenHelper class. You discovered you can also uninstall / reinstall the app, for the same results. If you are savvy with SQL you could ALTER the table. But whatever the method you must make sure that you app makes the schema changes before trying to access the new columns...
Also for SQLite:
_id integer primary key
is synonymous with:
_id integer primary key autoincrement not null
And queries use descending as the default order, so ORDER BY _id is the same as ORDER BY _id DESC.
Had the same problem, meaning it should have worked but didn't (had some typos in the create command that I fixed but that still didn't help). A colleague then told me to try clearing the data (just at AppInfo and then "Clear Data") which solved my problem, apparently the old database (that didn't work) was still there and had to be cleared out first.
I just put this answer here in case anybody else like me (android beginner) stumbles across this problem, because I went through dozens of stackoverflow threads with this problem but not one offered this possibility/solution and it bothered me for quite some time.
Did you add the definition of the _id column to your create statement later on, i.e. after the code had already been run once? Databases are persisted files, so if you modify the table structure in your code you need to make sure you clear your application's data so the database file can ge re-created with the correct table/column data.
Working through the Notepad tutorial and have a question. When it builds the database with the fields rowid, title, and body - all three are private static final strings. But when a method like fetchnote() runs, it uses a long id to get the note. How does the method get that id if rowid is a string? I would like to modify the tutorial a bit and have an activity call a listactivity. The listactivity will display everything in the database, then, when a user clicks an item, I would like to copy just that entry into a new database and send the row id back to the calling activity. But I can't figure out how to get it to copy the entry into the new database because the tutorial uses a row id to make adjustments and my new database would be blank. Does that make sense? I'm new to this.
I think the public static final Strings you're talking about are these:
public class NotesDbAdapter {
public static final String KEY_TITLE = "title";
public static final String KEY_BODY = "body";
public static final String KEY_ROWID = "_id";
...
}
If so, then these are the column names. The column names are strings, not the values in these columns. If you look a little bit further down you'll see:
private static final String DATABASE_CREATE =
"create table notes (_id integer primary key autoincrement, "
+ "title text not null, body text not null);";
Which is how the database is created. As you can see, the "_id" column is of type "integer", which I'm guessing is the same (or at least compatible with) long.
(Edit)
The following should enable you to copy the note out of the original table and insert it into another one.
mCursor = mDbHelper.fetchNote(rowId);
title = mCursor.getString(mCursor.getColumnIndex(NotesDbAdapter.KEY_TITLE));
body = mCursor.getString(mCursor.getColumnIndex(NotesDbAdapter.KEY_BODY));
// I'm using mDbHelper2 here to indicate that you're inserting it into
// a different table.
mDbHelper2.createNote(title, body);
// And if you want to remove it from the original table as well:
mDbHelper.deleteNote(rowId);
// I'm guessing you'd have to run this to square up all the rows in the display:
fillData();
You could just set up your new database with the same columns as the first one, but without an auto-incrementing parameter on the id column. Then you just add a new entry to it with the same details as the other one.