SQLite: primary key and sqlitedatabase.insert - sqlite

I have a table with an INTEGER PRIMARY KEY. That means, that my primary key becomes an alias for the rowid. However rowid is a long (according to the return value of SQLiteDatabase.insert) while my primary key is an integer. So what happens if I insert data at a time where all possible integer values are in use in that table? Since my primary key is an integer I would expect an error. But since my primary key refers to / is an alias for the rowid (which is a long -> still numbers available) SQLiteDatabase.insert could go ahead and insert my data, returning the rowid ... right? Or does SQLiteDatabase.insert restrict itself to the integer value range but still just returns a long every time? If so, wouldn't it make sense, to cast the return value of SQLiteDatabase.insert to int immediately? Because I could never get a "long-value" from that method anyway (due to my primary key integer)... or could I?

From: https://www.sqlite.org/datatype3.html
INTEGER. The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8
bytes depending on the magnitude of the value
As you can see in SQLite the INTEGER data type is not what int is in Java,
but it can store values up to 8 bytes just like the long type of Java.

Related

In SQLITE, does specifying the integer type for Primary Keys matter considering that primary keys must have unique values?

When someone asked the difference between integer types in SQLITE:
What is the difference between SQLite integer data types like int, integer, bigint, etc.?
The answer declared it unimportant for SQLITE because:
SQLite uses a more general dynamic type system. In SQLite, the datatype of a value is associated with the value itself, not with its container.
A SqlLite "integer" can hold whatever you put into it: from a 1-byte char to an 8-byte long long.
When I think of storing integers into a container, the signature of the container can vary with the same value.
0000 0000 0000 0001 = 1
0000 0001 = 1
Conversely an unsigned integer and an integer can have the same signature but different values:
1111 1111 1111 1111 1111 1111 1111 1111 = -1
1111 1111 1111 1111 1111 1111 1111 1111 = 4294967295
And so I am a bit confused whether it matters for primary keys if I specify the type, because the manual states:
A primary key is a field in a table which uniquely identifies the each rows/records in a database table. Primary keys must contain unique values. A primary key column cannot have NULL values.
From this, I have to assume that declaring the specific integer type for a column, being one of:
INT
INTEGER
TINYINT
SMALLINT
MEDIUMINT
BIGINT
UNSIGNED BIG INT
INT2
INT8
is important because I assume that datatypes can have exact same signatures with different values and vice versa, thus violating the must contain unique values specification somehow.
So ultimately my question is, will declaring the datatype of a primary key to be specifically be one of TINYINT SMALLINT, MEDIUMINT, BIGINT, UNSIGNED BIG INT, INT2, INT8 make any difference whatsoever?
I need to know this because I am making a key value store with SQLITE, and want to have the ability to set keys for all the possible datatypes. If there is no difference between TINYINT and INTEGER, then I won't bother having TINYINT as a possible key datatype.
The column type INT, INTEGER, WHATEVER (you can specify virtually any column type) has little bearing, it's an indication of what is to be stored in the column. However, it does not set the type as with one exception (to be discussed) of data that can be stored. In short any type (bar the exception) of data can be stored in any column (irrespective of the defined column type).
see 3.1 Determination of Column Affinity in the link below
SQL does not differentiate between stored values other than the storage class (null,integer,real,text,blob), if stored as an INTEGER then it is an integer bound only by the limitations of it being stored in at most 8 bytes (64 bit signed).
see 2. Storage Classes and Datatypes in the link below
The exception is the use specifically of INTEGER PRIMARY KEY or INTEGER with the column set as the primary key at the table level. The value stored MUST be an integer otherwise a DATATYPE MISMATCH will occur.
as per Any column in an SQLite version 3 database, except an INTEGER PRIMARY KEY column, may be used to store a value of any storage class. ( also in 2. Storage Classes and Datatypes)
So ultimately my question is, will declaring the datatype of a primary key to be specifically be one of TINYINT SMALLINT, MEDIUMINT, BIGINT, UNSIGNED BIG INT, INT2, INT8 make any difference whatsoever?
Not with the listed types (TINYINT ....) as the types all contain INT they will have a type affinity of INTEGER and the column will NOT be an alias of the rowid column.
If you included INTEGER in the list then YES it will make a difference as the column will then be an alias of the rowid column (i.e. it is INTEGER PRIMARY KEY). The column will also be restricted to being an integer value (the columns using the other listed types will not be restricted to integer values).
You may wish to refer to Datatypes in SQLite
The following SQL demonstrates some of the above:-
DROP TABLE IF EXISTS example;
CREATE TABLE IF NOT EXISTS example (
rowid_alias_must_be_unique_integer INTEGER PRIMARY KEY, -- INTEGER PRIMARY KEY makes the column an alias of the rowid
col_text TEXT,
col_integer INTEGER,
col_real REAL,
col_BLOB BLOB,
col_anyother this_is_a_stupid_column_type -- will have a type affinitiy of NUMERIC
);
/* INSERTS first row with a negative rowid */
INSERT INTO example VALUES (-100,'MY TEXT', 340000,34.5678,x'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff',100);
/* All subsequent inserts use the generated rowid */
/* the same value is inserted into all the other columns */
INSERT INTO example (col_text,col_integer,col_real,col_blob,col_anyother) VALUES
('MY TEXT','MY TEXT','MY TEXT','MY TEXT','MY TEXT'),
(100,100,100,100,100),
(34.5678,34.5678,34.5678,34.5678,34.5678),
(x'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff',x'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff',x'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff',x'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff',x'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff')
;
SELECT
*,
rowid,
typeof(rowid_alias_must_be_unique_integer),
typeof(col_text),
typeof(col_integer),
typeof(col_real),
typeof(col_blob),
typeof(col_anyother)
FROM example
;
/* WILL FAIL as rowid alias is not an integer */
INSERT INTO example VALUES('a','a','a','a','a','a');
DROP TABLE IF EXISTS example;
The result of the first SELECT will be :-
Note that blobs are handled/displayed according to how the tool (Navicat for SQLite) handles the display of blobs.
The last INSERT fails because the value being inserted into the rowid alias is not an integer value e.g. :-
/* WILL FAIL as rowid alias is not an integer */
INSERT INTO example VALUES('a','a','a','a','a','a')
> datatype mismatch
> Time: 0s
Note that the answer has not dealt with the intricacies of how the column affinity may effect the extraction of data.

Replacing sqlite.net "bigint" with integer for autoincrement primary key constraint

We are using sqlite for our mobile project - however when we try to use our long id variables as primary key autoincrement - the code is giving error "Autoincrement is only allowed in integer primary key". When I checked online about this error, I found out that autoincrement in sqlite is not allowed for bigint types.
The interesting thing is bigint is implemented by integer as well - sqlite has no bigint type - it just uses the size of the integer to decide whether it is a bigint or not.
http://www.sqlite.org/datatype3.html
To fix this - I replaced the open source code where it creates the table with "bigint" with "integer", and wrote some test code to verify that it works outside the boundaries of regular integer (added an item with id larger than the integer range on purpose and continuously added 10 more objects).
It seems like it is working now - yet I want to know if it can cause some other issues. We are syncing the mobile app ids to our db ids, and thus we will definitely have ids larger than the normal integer range.
Is this solution a valid solution? What kind of troubles could this cause?
From Datatypes In SQLite Version:
If the declared type contains the string "INT" then it is assigned INTEGER affinity.
However, this does not mean that a column with INTEGER affinity is suitable for auto-increment duty! The special case is explicitly drawn out in ROWIDs and the 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.
The maximum size of an INTEGER value in SQLite is 8 bytes (64bits, signed two's complement) and thus maps cleanly to a long value in .NET (or Java); it doesn't matter if the column was declared as "INTEGER", which it must be for an auto-increment column, or with "BIGINT". (The actual type is per value, not per column; however, auto-increment/ROWID values will all be integers.)
Also, SQLite automatically has a "record/row identifier", even without explicitly creating such a column - this is the "ROWID" column and can be accessed as ROWID, _ROWID_, or OID unless shadowed. See SQLite Autoincrement if this is a suitable option, as it changes the algorithm and removes some monotonically increasing guarantees:
These are important properties in certain applications. But if your application does not need these properties, you should probably stay with the default behavior since the use of AUTOINCREMENT requires additional work..

SQLite integer size: individually sized or for the entire group

Taken straight off of SQLite's site "The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value."
Does this mean that if you have 1 value that requires 8 bytes, ALL values in that column will be treated as 8 bytes. Or, if the rest are all 1 byte, and one value is 8 bytes, will only that value be using 8 bytes and the rest will remain at 1?
I'm more used to SQL in which you specify the integer size accordingly.
I know the question seems trivial, but based on the answer will determine how I handle a piece of the database.
The sqlite database structure is different in the way it handles data types. Each field can have a different type...
Here is the documentation from sqlite:
Most SQL database engines use static typing. A datatype is associated with each column
in a table and only values of that particular datatype are allowed to be stored in that
column. SQLite relaxes this restriction by using manifest typing. In manifest typing, the
datatype is a property of the value itself, not of the column in which the value is
stored. SQLite thus allows the user to store any value of any datatype into any column
regardless of the declared type of that column. (There are some exceptions to this rule:
An INTEGER PRIMARY KEY column may only store integers. And SQLite attempts to coerce
values into the declared datatype of the column when it can.)

Records count exceeded maximum integer value

Let's say we have a table in SQL Server with primary key of integer type.
What happens if the number of rows in table exceeds maximum value for int data type?
It looks as though your database will refuse to create a new row. Your best bet if you are looking at that barrier is to switch to Bigint, Decimal or using a GUID as the datatype for the primary key:
http://mssqlserver.wordpress.com/2006/12/01/what-happens-when-my-integer-identity-runs-out-of-scope/

Can I use anything other than BIGINT as Primary Key data type in SQLite?

I was psyched about the possibility of using SQLite as a database solution during development so that I could focus on writing the code first and dynamically generating the db at runtime using NHibernate's ShemaExport functionality. However, I'm running into a few issues, not the least of which is that it seems that SQLite requires me to use Int64 for my primary keys (vs, say, Int32 or Guid). Is there any way around this?
Note: I should specify that this is in the context of an app using NHibernate. It is not strictly speaking the case that one can't create a table in SQLite with an INT datatype, but the behavior when you save and retrieve the data seems to indicate that it's being stored and/or retrieved as Int64.
SQLite will let you use any field in your table as a PRIMARY KEY. Doing so will implicitly create a UNIQUE index on the field. This is then the field that you, as a developer, can consider to be the primary unique identifier for the field. It can be any supported SQLite data type (below).
SQLite will always create an implicit internal numeric identifier for every table. It will have several aliases including RowID, OID, and _ROWID_. If you create your primary key as INTEGER PRIMARY KEY then it will use the same field as your primary key and SQLite's internal numeric identifier.
SQLite doesn't have a concept of Int32 or Int64 or Guid data types. It only has four data types: INT, REAL, TEXT, and BLOB. When you run DDL against SQLite if you use anything other than these four identifiers, SQLite will use a set of rules to determine which type to use. Basically, Int32 and Int64 are treated as aliases of INT and end up doing the exact same thing.
Even once you've created the tables with the data types you mentioned for each field, all you set is the type affinity for that field. SQLite does not enforce data types. Any data can be put into any field regardless of the declared type. SQLite will use the type affinity to convert data if possible, so if you insert '123' as a text string into an INT field, it will store it as the number 123.
The only exception to the type affinity is INTEGER PRIMARY KEY FIELDS. Those must be integers.
Integers in SQLite are always stored with a variable length field. So depending on the size of the integer, you may actually get an Int32 back for some rows an Int64 for others, all within the same field. This depends on the wrapper you're using, in this case NHibernate (I guess with System.Data.SQLite).
It does not require you to use Int64, however, it is possible that it only allows that when you specify a numeric primary key. Because sqlite doesn't really have referential integrity checking (though there has been recent discussion of this and perhaps dr hipp has even implemented, i haven't checked lately), all primary key means is "Make this column unique and create an index on it". there isn't much special about it. You can certainly use varchar or text for a primary key. for example, this works:
create table t_test (
theID varchar(36) primary key,
nm varchar(50)
)
in the above you could use theID to store a guid in text form.
More info can be found here: http://www.sqlite.org/lang_createtable.html#rowid
#weenet ... per your comments, the following code works just fine.
i think you need to post your code if you're still having troubles.
create table t_test2 (
theID int32 primary key,
nm varchar(50)
);
insert into t_test2 (theID, nm) values (1, 'don');
insert into t_test2 (theID, nm) values (2, 'weenet');
select * from t_test2;
additionally, this code works fine (varchar as a primary key):
create table t_test (
theID varchar(36) primary key,
nm varchar(50)
)
insert into t_test (theID, nm) values ('abcdefg', 'don');
insert into t_test (theID, nm) values ('hijklmnop', 'weenet');
select * from t_test

Resources