SQLite PRIMARY key AutoIncrement doesn't work - android

I'm trying to a have a table with an auto incremented primary key. The SQL query for table creation is included.
Problem is the auto-increment does not work. Meaning when I insert a row with NULL as the value of conversation_id it just inserts null. I have this problem on multiple tables.
-- Table: conversations
CREATE TABLE conversations (
conversation_id INTEGER (64) PRIMARY KEY
UNIQUE,
target_id BIGINT (64),
sender_id BIGINT (64),
status STRING (16) NOT NULL
DEFAULT unseen,
is_group INTEGER (1) NOT NULL
DEFAULT (0),
last_update INTEGER (32) DEFAULT (0),
target_name STRING (64),
target_photo STRING (256),
unread_count INTEGER (10) DEFAULT (0),
last_message STRING (256)
);
The following is the method I use to insert into table:
public Conversation addConversation(Conversation conversation) {
SQLiteDatabase db = getWritableDatabase();
ContentValues row = new ContentValues();
row.put("target_id", conversation.getTargetID());
row.put("sender_id", conversation.getSenderID());
row.put("target_name", conversation.getTargetName());
row.put("target_photo", conversation.getTargetPhoto());
row.put("status", conversation.getStatus());
row.put("unread_count", conversation.getUnreadCount());
row.put("last_message", conversation.getLastMessage());
conversation.setConversationID(db.insert(TBL_CONVERSATIONS, null, row));
Log.d(TAG, "conversation added: "+conversation.getConversationID());
db.close();
return conversation;
}
The curious thing here is when I retrieve the insert id from insert method it returns the correct value, but the actual database field is null.
If I understand correctly A column declared INTEGER PRIMARY KEY will autoincrement. [Cite]

From documentation:
A table created using CREATE TABLE AS has no PRIMARY KEY and no
constraints of any kind. The default value of each column is NULL.
You don't have to add UNIQUE constraint on a COLUMN that has PRIMARY KEY constraint.
Explanation:
A UNIQUE constraint is similar to a PRIMARY KEY constraint, except
that a single table may have any number of UNIQUE constraints.
Instead add NOT NULL.
This is why:
According to the SQL standard, PRIMARY KEY should always imply NOT
NULL. Unfortunately, due to a bug in some early versions, this is not
the case in SQLite. Unless the column is an INTEGER PRIMARY KEY or the
table is a WITHOUT ROWID table or the column is declared NOT NULL,
SQLite allows NULL values in a PRIMARY KEY column. SQLite could be
fixed to conform to the standard, but doing so might break legacy
applications. Hence, it has been decided to merely document the fact
that SQLite allowing NULLs in most PRIMARY KEY columns.
I recommend using this Column definition:
CREATE TABLE conversations (
conversation_id INTEGER PRIMARY KEY NOT NULL AUTOINCREMENT,
...
}

Most likely the return value you are seeing is the row's ROWID. A ROWID is a hidden column available in every table, unless explicitly removed. According to the official documentation, when you define an INTEGER PRIMARY KEY, it should automatically become an alias for the ROWID. That's also why AUTOINCREMENT is not needed when you define your column in this way.
With one exception noted below, if a rowid table has a primary key
that consists of a single column and the declared type of that column
is "INTEGER" in any mixture of upper and lower case, then the column
becomes an alias for the rowid. Such a column is usually referred to
as an "integer primary key". A PRIMARY KEY column only becomes an
integer primary key if the declared type name is exactly "INTEGER".
Other integer type names like "INT" or "BIGINT" or "SHORT INTEGER" or
"UNSIGNED INTEGER" causes the primary key column to behave as an
ordinary table column with integer affinity and a unique index, not as
an alias for the rowid.
See: CREATE TABLE documentation
Either your column is not an INTEGER, or it is not a PRIMARY KEY. Taking a closer look at your create-statement I can see one or two possible culprits.
UNIQUE vs. PRIMARY KEY
A primary key is unique by default. According to the syntax definition (which you can find on the same documentation page as the citation above) you should choose either PRIMARY KEY or UNIQUE, not both.
COLUMN length restrictions
ROWID is already 64-bit by default. You have specified length 64, but lengths are not specified in bits. You may have specified a 64-byte integer here, which I'm sure was not intended. This should actually not be a problem however, since SQLite ignores length-constraints. So it is not meaningful to specify them.
TLDR
Replace this code:
conversation_id INTEGER (64) PRIMARY KEY UNIQUE
With this:
conversation_id INTEGER PRIMARY KEY AUTOINCREMENT

I just put autoincrement in the query and it works fine .
like this
id integer primary key autoincrement

Related

SQLite Autoincrement not inserting

I have an SQLite Database and when I insert the ID should be automaticly incrementet with AUTOINCREMENT.
But it is always null.
This is the create table
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE ausgaben (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, TAG text,DATUM text, AUSGABE text, MENGE text, KATEGORIE text)");
}
And this is how I insert data:
public boolean insertAusgabe(String tag, String datum, String ausgabe, String menge, String kategorie){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.putNull("id");
contentValues.put(AUSGABEN_TAG,tag);
contentValues.put(AUSGABEN_DATUM,datum);
contentValues.put(AUSGABEN_AUSGABE,ausgabe);
contentValues.put(AUSGABEN_MENGE,menge);
contentValues.put(AUSGABEN_KATEGORIE,kategorie);
db.insert(TABLE_NAME,null,contentValues);
return true;
}
If I understand right, this should work correctly.
But the database looks like this:
It would appear that you are expecting the id column to be null rather than a number.
If you code id INTEGER PRIMARY KEY NOT NULL AUTOINCREMENT (see note about AUTOINCREMENT below) then that column is a special column that is an alias of the rowid column (unless the table has been defined using WITHOUT ROWID).
The rowid column cannot be null and must be a integer value. If an attempt is made to insert a row where the value for the column is null (or not specified) then SQLite will assign an integer value (long for java). 1 if there are no rows in the table then 1 greater than the highest number used.
Hence why you have a sequence of numbers in the id column.
If, for example the table were defined using id INT PRIMARY KEY NOT NULL then, the id column IS NOT an alias of the rowid column. (AUTOINCREMENT can then not be used as it can only be used for an alias of the rowid column) Then none of the inserts would work as the value for the id column would be NULL which due to the coding of the NOT NULL constraint will result in a constraint conflict.
However if the column were defined using id INT PRIMARY KEY, then null values for the id would be allowed. Noting that coding PRIMARY KEY, implies a UNIQUE constraint, that is all values must be UNIQUE. SQLite considers all NULL values as being unique in comparison to each other.
So the last definition would allow what appears to be your expected result. However, what use would an indeterminate value be for the purpose of identifiyting a row? (that's rhetorical).
As such the result you initially obtained, is the more useful result. Even if not intended.
A note on AUTOINCREMENT
AUTOINCREMENT is very likely not needed, this specifies an extension of the rowid determination algorithm in that it
enforces the latest rowid value being greater than any existing or used rowid,
that is it relies upon another table, namely sqlite_sequence to record the highest allocated rowid and then it uses the higher of the highest existing rowid or the value stored for the table in the sqlite_sequence table.
With AUTOINCREMENT when the highest possible value (9223372036854775807) has been assigned and an attempt is made to insert a new row. Then an SQLITE_FULL error will result. Without, attempts are made to use an random unused value (e.g. if rows have been deleted).
With AUTOINCREMENT there is an overhead (something like 8-12% according to What are the overheads of using AUTOINCREMENT for SQLite on Android?).
NOTE
It should be noted that there is no gaurantee that the rowid, with or without the AUTOINCREMENT keyword will increase by 1. There are some situations where values may be skipped as per
Note that "monotonically increasing" does not imply that the ROWID always increases by exactly one. One is the usual increment. However, if an insert fails due to (for example) a uniqueness constraint, the ROWID of the failed insertion attempt might not be reused on subsequent inserts, resulting in gaps in the ROWID sequence. AUTOINCREMENT guarantees that automatically chosen ROWIDs will be increasing but not that they will be sequential.
SQLite Autoincrement
*In short it is not wise to have any expectation of the * id/rowid column to be anything other than a means of efficiently identifying a row.

Is there any way to make Room generate an autogenerating primary key without AUTOINCREMENT?

My Room database has the following entity:
#Entity
public class SmsMessage {
#PrimaryKey
public long id;
public boolean incoming;
public String sender;
public String receiver;
public String body;
public long timestamp;
}
This fails with the following RuntimeException when trying to insert more than one item into the database:
SQLiteConstraintException: PRIMARY KEY must be unique (code 19)
The generated SQL CREATE TABLE statement looks like this:
CREATE TABLE `SmsMessage` (
`id` INTEGER NOT NULL,
`incoming` INTEGER NOT NULL,
`sender` TEXT,
`receiver` TEXT,
`body` TEXT,
`timestamp` INTEGER NOT NULL,
PRIMARY KEY(`id`)
);
This seems to be different from INTEGER NOT NULL PRIMARY KEY, even though I can't find any documentation for this behaviour in the SQLite documentation.
It seems I have to use #PrimaryKey (autogenerate=true) in order to make Room automatically generate primary key values. Looking at the generated database when using autogenerate=true, this generates the following SQL:
CREATE TABLE `SmsMessage` (
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`incoming` INTEGER NOT NULL,
`sender` TEXT,
`receiver` TEXT,
`body` TEXT,
`timestamp` INTEGER NOT NULL
);
It seems that autogenerate=true corresponds to SQLite AUTOINCREMENT. However, the SQLite documentation makes quite clear that AUTOINCREMENT isn't needed (and in most cases not recommended) in order to automatically generate unique primary keys. The purpose of AUTOINCREMENT is basically to prevent re-use of used but deleted primary keys.
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed.
In SQLite, a column with type INTEGER PRIMARY KEY is an alias for the ROWID (except in WITHOUT ROWID tables) which is always a 64-bit signed integer.
On an INSERT, if the ROWID or INTEGER PRIMARY KEY column is not explicitly given a value, then it will be filled automatically with an unused integer, usually one more than the largest ROWID currently in use. This is true regardless of whether or not the AUTOINCREMENT keyword is used.
If the AUTOINCREMENT keyword appears after INTEGER PRIMARY KEY, that changes the automatic ROWID assignment algorithm to prevent the reuse of ROWIDs over the lifetime of the database. In other words, the purpose of AUTOINCREMENT is to prevent the reuse of ROWIDs from previously deleted rows.
So it seems that using #PrimaryKey(autogenerate=true) is usually not needed nor recommended. But only using #PrimaryKey alone will not automatically generate values at all.
How do I tell Room that what I want is 'id' INTEGER NOT NULL PRIMARY KEY?
That's not possible at the moment - the only option is AUTOINCREMENT. You can star the existing feature request for progress updates on support for this use case.

NOT NULL Constraint not working on the PRIMARY KEY column in SQLite android

I'm trying to build a simple application where the user isn't allowed to insert duplicate values.
I'm trying the Primary Key as one of the constraints and NOT NULL also. But NOT NULL doesn't seem to work.
I tried my best but failed to resolve it. Please help
((ID INTEGER NOT NULL PRIMARY KEY)";)
SQLite allows NULL values in the PRIMARY KEY column except that a column is INTEGER PRIMARY KEY column or the table is a WITHOUT ROWID table or the column is defined as a NOT NULL column.
For example:
CREATE TABLE suppliers(
supplier_id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
Because this column is declared as INTEGER PRIMARY KEY, it will not accept NULL values.
Try this format:
sqlite> CREATE TABLE DEPARTMENT(
ID INT PRIMARY KEY NOT NULL,
DEPT CHAR(50) NOT NULL,
EMP_ID INT NOT NULL
);
Try to use an UNIQUE constraint on the corresponding fields.
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 contain a unique combination of values in the columns identified by the UNIQUE constraint. For the purposes of UNIQUE constraints, NULL values are considered distinct from all other values, including other NULLs.
Edit:
According to your additional info you can use a CHECK constraint to prevent blank values.
sqlite> create table foo (bar TEXT, CHECK(bar <> ''));
See this answer for more information: Not empty string constraint in SQLite

What is the use of BaseColumns's _ID primary key in Android

It is a bit confusing.... If I implement BaseColumns for each of my tables it automatically create an autoincremented primary key for me called _ID but then Do I need to create my own primary keys for each of my tables as well or is it redundant or even not necessary?
In case I need my own primary key, let's say, _MyID, I guess the primary key will be formed by _ID and my own (_MyID), right? so in this case, it would be possible to insert the more than one register with the same _MyID.... as _ID is autoincremented automatically, that is:
_ID _MyID Other Fields.....
1 1000 ....
2 1000 ....
3 1000 ....
... and so on
so in this case, how to control that only one register can have the value 1000 for _MyID?
Also, I guess I can use _ID column to act as a foreign key with other tables, right?
The main use for BaseColumns._ID is that Android's CursorAdapter will look for that column name in the cursor you give it. There may be other classes that do the same, but I can't think of any off the top of my head. If you aren't using CursorAdapter, then there's really nothing binding you to using _id as a column name in your table and you can name the column however you like.
it automatically create an autoincremented primary key for me called _ID
There is nothing automatic about it based on what you've shown so far. You will only have such a column if you executed SQL like this:
CREATE TABLE tableName (_id INTEGER PRIMARY KEY AUTOINCREMENT, ...);
You can just as easily leave it out or give a different name to the primary key. Furthermore, there is nothing that requires your primary key to be auto-incremented; as long as the values are unique, it satisfies the primary key requirement. In other words, this is fine too:
CREATE TABLE tableName (_id INTEGER PRIMARY KEY, ...);
In case I need my own primary key, let's say, _MyID, I guess the primary key will be formed by _ID and my own (_MyID), right?
Not quite. You would have to do something like this:
CREATE TABLE tableName (_id INTEGER, _myId INTEGER, ..., PRIMARY KEY(_id, myId));
This creates a composite key, but note that neither of the two columns are themselves declared as primary key. Honestly though, if you don't need such an arrangement, then stick to one primary key.
One last thing:
If you are planning to use CursorAdapter, you might want to name the column _id for convenience, but even then you don't have to. All that matters is the cursor has a column by that name. The actual column in the table can have a different name, you just have to alias it at query time so that it has the proper name in the cursor:
SELECT _myId as _id, ... FROM ...;

Problem with inserting into android sqlite3 table that has composite primary key

I have a table with a composite primary key and I am having trouble inserting. The code used to create the table is:
CREATE TABLE ClassEvent (
EventName varchar(10) NOT NULL,
CourseId varchar(10) NOT NULL,
EventType varchar(20),
EventWeight number(3),
DueDate DATE NOT NULL,
FOREIGN KEY (CourseId) REFERENCES Courses(CourseId),
PRIMARY KEY (CourseId, EventName));
The problem I am having is when I want to insert records that have values that may not be unique for the columns CourseId or EventName, but are a unique combination of the 2.
for example, if I try to run the following 2 inserts:
INSERT INTO ClassEvent VALUES('Assignment 1','60-415','Assignment',10,'12/10/2010');
INSERT INTO ClassEvent VALUES('Project 1','60-415','Project',15,'5/12/2010');
I get the following error:
Error: columns CourseId, EventName are not unique.
and the second insert does not make it into the DB. Why does this error out? I thought that a composite primary key requires that the combination of both values are unique. In my above inserts, the values for the EventName column are different even though the values for CourseId are the same. Shouldn't this be seen as 2 unique combinations and thus 2 different primary keys?
My table needs to be able to hold several different events for each CourseId, but each Event must be unique for each Course. I need to be able to insert values into the table like:
EventName CourseId
Assignment 1 60-415
Project 1 60-415
Assignment2 60-415
Project 2 60-415
Assignment 1 60-367
Project 1 60-367
and so on. Can anyone tell me how I can get this to work? Why are these composite PK's not being seen as unique entries? Any help would be much appreciated.
Here is the java function I am using for the insert:
public void addNewClassEvent(ContentValues values) {
SQLiteDatabase db = openConnection();
db.insert("ClassEvent", null, values);
db.close();
}
Could this be causing the problem?
You can have a composite primary key in SQLite, but you
have to create the key when you create the table:
CREATE TABLE example1(
field1 FLOAT,
field2 TEXT,
PRIMARY KEY(field1, field2)
);
You cannot create the primary key after the fact using ALTER TABLE.
On the other hand, you can create a UNIQUE INDEX after the fact
which has essentially the same effect as a PRIMARY KEY:
CREATE UNIQUE INDEX pk_index ON "table1"("field1","field2");
I am not sure how you have created, the tables, and if you have added the primary index later, but grab the database to your desktop, and check out how works in a desktop environment.
You can't make combinations like that, but you don't need them. What is stopping you from just having a truly id column ?

Categories

Resources