SQLite Autoincrement not inserting - android

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.

Related

SQLite - integer PRIMARY INDEX constraint failing?

In my Android app, I create a FULLTEXT table like this:
CREATE VIRTUAL TABLE products USING fts3 (
_id integer PRIMARY KEY,
product_name text NOT NULL,
...
)
And I add this index:
CREATE INDEX product_name_index ON products (product_name)
The app populates the table with various products, each with a unique _id value.
However, when I then try to insert an already-existing product ID (using an _id value that is already in the table, but with a different product_name value) like this:
long rowId = db.insertOrThrow("products", null, contentValues);
a new row is added to the table (with a brand new rowId value returned)!
I expected the insertOrThrow command to fail, so where am I going wrong? Is it something to do with the fact that it's a FULLTEXT table or could the index I specified on the product_name column be messing things up somehow?
I read this section about INTEGER PRIMARY KEY, but unfortunately I'm none the wiser.
Update
When I try to perform the same operation on a standard (non-FULLTEXT) table, then the insertOrThrow command results in the expected SQLiteConstraintException.
I think the issue might be that an FTS table has the concept of a docid and a rowid column and specifying null for the docid results in that being given a value.
as per :-
There is one other subtle difference between "docid" and the normal
SQLite aliases for the rowid column.
Normally, if an INSERT or UPDATE
statement assigns discrete values to two or more aliases of the rowid
column, SQLite writes the rightmost of such values specified in the
INSERT or UPDATE statement to the database.
However, assigning a
non-NULL value to both the "docid" and one or more of the SQLite rowid
aliases when inserting or updating an FTS table is considered an
error. See below for an example.
1.3. Populating FTS Tables

How can i delete every value row in my SQLite Table?

I want to delete all my values in my sqlite Table.
When I click my button it deletes everything besides the id it keeps counting
so for example:
ID, FIRSTNAME, LASTNAME
1, Jack Sparrow
2, Johhny Dep
if I press delete and add new values, it shows this
ID, FIRSTNAME, LASTNAME
2, Obama Barack
3, Mike Tyson
this is my method
private void DeleteEverything()
{
SQLiteDatabase db = mDbHelper.getWritableDatabase();
db.execSQL("delete from "+NamesContract.NameEntry.TABLE_NAME);
}
You should consider to drop and recreate the table.
You can find an example here
If you specify ?? INTEGER PRIMARY KEY or ?? INTEGER PRIMARY KEY AUTOINCREMENT then SQlite treats this in a specific way. That is ?? (by the looks of it ID in your case) is an alias for SQLite's rowid.
rowid if not specified when inserting a row will automatically be generated and is in general guaranteed to be unique and is normally incremented by one. However, it is not guaranteed to be incremented nor incremented by one.
If you specify AUTOINCREMENT (i.e. the latter of the two above) then the guarantee, is that a new rowid will be greater but not necessarily by 1.
There is a limit of 9223372036854775807 on the value of rowid. If AUTOINCREMENT is not used and this limit has been reached then an attempt will made to utilise free numbers (e.g. the rowid of rows that have been deleted). If AUTOINCREMENT is specified and the limit has been reached then an insert will fail with an SQLITE_FULL error.
As such, in your case the freed ID's from deleting rows will not be reused.
In short you should never rely upon the rowid (or an alias of it) column being a specific value, rather you should rely upon it just being a unique value purely for the purpose of uniquely identifying a row (and perhaps the fastest way of accessing a row).
You can, albeit it inadvisable, set rowid either by say INSERT INTO mytable (rowid, myothercolumn) VALUES(1, 'other data') or if ID has been used as an alias then INSERT INTO mytable (ID, myothercolumn)VALUES(1, 'other data').
If you really need the first row to be 1 and then 2 and so on then you could DROP the table and then recreate it rather then deleting all rows. e.g. DROP TABLE mytable, followed by CREATE TABLE mytable (ID INTEGER PRIMARY KEY, FIRSTNAME TEXT, LASTNAME TEXT). However, I'd suggest you will just end up with issues which will subsequently be far more complicated to determine and correct.
If you think about it, really what does Johnny Depp being 2 signify rather than Barack Obama being 2? If it is, for example, a popularity rating the surely you'd have some means of determining the popularity and input that directly rather than input it somewhere else to then be used to determine the insertion order and then do the inserts.
Much of the above is basically a summary of SQLite Autoincrement
As an example of unpredictability, a table was created with:-
CREATE TABLE mytable (ID INTEGER PRIMARY KEY, FIRSTNAME TEXT, LASTNAME TEXT)
A row was inserted using INSERT INTO mytable (ID, FIRSTNAME, LASTNAME) VALUES(9223372036854776000,'FRED','BLOGGS'). Note the use of 9223372036854776000 forces above the limit processing.
This was then followed by a number of INSERT INTO mytable(FIRSTNAME, LASTNAME) VALUES('????','????') inserts. Note! without ID, so using SQLITE's unique identifer determination (above the limit processinmg). The resultant table :-
MARY QUITECONTRARY was the 2nd row inserted,TOM SMITH the 3rd. The two RICHARD BURTON rows are an example of where the unique id could be essential for determining a specific row, both were also inserted without specifying the insertion order.
Note! if the above were tried but with AUTOINCREMENT specified, then the second insert, and any subsequent inserts, would fail with an SQLITE_FULL error.

SQLite PRIMARY key AutoIncrement doesn't work

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

Android SQLite Database After delete table, will the id starts from 1?

I have a doubt that if i delete the table using following statements,
SQLiteDatabase db = this.getWritableDatabase();
db.delete(date_difference, null, null);
then if i'm inserting a row as a fresh and first record into the table, will the id primary key auto increment of the records starts from 1 ?
If no ROWID is specified on the insert, or if the specified ROWID has
a value of NULL, then an appropriate ROWID is created automatically.
The usual algorithm is to give the newly created row a ROWID that is
one larger than the largest ROWID in the table prior to the insert.
If the table is initially empty, then a ROWID of 1 is used. If the largest ROWID is equal to the largest possible integer
(9223372036854775807) then the database engine starts picking positive
candidate ROWIDs at random until it finds one that is not previously
used.
So yes, after you delete the table, IDs will start from 1
http://www.sqlite.org/autoinc.html
The documentation provided states that that delete method is a:
Convenience method for deleting rows in the database.
The syntax is:
int delete(String table, String whereClause, String[] whereArgs)
Therefore it won't start from 1 again. It'll continue on from the last increment. If you deleted the whole table, then re-created it, the increment would begin at 1.
SQLite keeps track of the largest ROWID that a table has ever held using an internal table named "sqlite_sequence". The sqlite_sequence table is created and initialized automatically whenever a normal table that contains an AUTOINCREMENT column is created**.
The content of the sqlite_sequence table can be modified using ordinary UPDATE, INSERT, and DELETE statements**. But making modifications to this table will likely perturb the AUTOINCREMENT key generation algorithm. Make sure you know what you are doing before you undertake such changes.
So when you delete your table and you re-create it, you should make the SQLITE_SEQUENCE restart from 0.
You should do something like this :
Delete from date_difference;
Delete from sqlite_sequence where name='date_difference';
Care because the field 'table name' in where clause is case sensitive.
Read this for more informations.
Define the primary key field as
INTEGER PRIMARY KEY AUTOINCREMENT DEFAULT 1
Then remove the code if you are doing insertion of any value for the primary key field like following
values.put(KEY_PRIMARY, object.getIntegerValue());

Clarification on the row_id returned by sqlite's insert statement

Ok, we see here that the SQLite insert statement returns something called row id.
When inserting, we know that SQLite likes a primary key column called ID and will auto-generate an appropriate value if the ID value being inserted is null.
The glue that's missing is confirmation that the row id returned by the SQLiteDatabase.insert() method is in fact the value that was auto-generated for the row's primary key. (This is not the case for Oracle...)
Will someone confirm or deny, please?
Resolution
Ok, so from the link posted by #sixfeetsix:
Each entry in an SQLite table has a unique 64-bit signed integer key called the "rowid". The rowid is always available as an undeclared column... If the table has a column of type INTEGER PRIMARY KEY then that column is another alias for the rowid.
Then his auto-increment link verifies that the row_id and the auto-increment value will be the same value.
That's a wrap, thanks, folks!
The Android insert(...) method returns:
the row ID of the newly inserted row, or -1 if an error occurred
This can be confusing to the uninitiated, but the row id in sqlite is either the integer primary key field, or, in its absence, a 64-bit undeclared column named ROWID.
So when you insert either NULL or no value in an auto-increment field, the generated value is returned.
They are two separate things.
The row_id returned by insert() is the unique identifier for the row inserted, and it's an identifier used internally by SQLite.
On the other hand, you can have a column in the table that represents your business' key, declared:
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
So SQLite will generate values for that column for you.
As far as I see, there is no such function in SQLite like SQL Server's SCOPE_IDENTITY() (which returns the last autoincremental inserted id).
So, if you made an insert and need to retrieve the auto-generated id, you can do something like:
SELECT seq FROM sqlite_sequence WHERE name='tableName'
But take note that this is not a safe way in a concurrent scenario.

Categories

Resources