Use composite primary key and autoincrement one column if null - sqlite

My table:
CREATE TABLE methods (
id INTEGER NOT NULL,
version INTEGER NOT NULL,
text TEXT NOT NULL,
PRIMARY KEY (id,version)
);
I would like the column id to auto-increment when null on a new row. I would also like the column version to be set to 1. I would like to store the auto-increment value in sqlite_sequence. When a row is inserted with id I would like version to increase by 1.
I have this at application level but need it at the database level and understand I will have to create a trigger or two.

Related

Unique columns pair constraint in SQLite with conflict strategy

I need to create a unique constraint for two columns in a row with conflict strategy attached. Suppose we have a table:
CREATE TABLE `telephones`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL);
So it is clear that it is a separate table for one-to-many relationship between a user and his telephones. What I need is to create a unique index for user_id and telephone, so database shouldn't have duplicates.
AFAIK, here are two ways of creating such a constraint: either by creating an index as a separated SQL request or by creating a constraint inside CREATE TABLE statement. First way looks like this:
CREATE UNIQUE INDEX `user_ids_and_telephones` ON `telephones`(`user_id`, `telephone`) ON CONFLICT IGNORE
And the second way looks like this:
CREATE TABLE `telephones`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL,
UNIQUE(`user_id`, `telephone`) ON CONFLICT IGNORE);
My question is: are these ways equivalent and will both work correctly for the goal described, or do they have some logical differences that will affect subsequent duplicates inserting logic?
I didn't find documentation quiet clear about that.
Both ways create an index and as such they act in the same way (see below). The documentation states this as :-
In most cases, UNIQUE and PRIMARY KEY constraints are implemented by
creating a unique index in the database. (The exceptions are INTEGER
PRIMARY KEY and PRIMARY KEYs on WITHOUT ROWID tables.) Hence, the
following schemas are logically equivalent:
CREATE TABLE t1(a, b UNIQUE);
CREATE TABLE t1(a, b PRIMARY KEY);
CREATE TABLE t1(a, b);
CREATE UNIQUE INDEX t1b ON t1(b);
SQL As Understood By SQLite - CREATE TABLE - SQL Data Constraints
However, I do not believe that you can code a conflict clause when defining an index independently. So
CREATE UNIQUE INDEX user_ids_and_telephones ON telephones(user_id, telephone) ON CONFLICT IGNORE is not valid.
As such, the conflict handling will differ.
For example consider the following :-
DROP TABLE IF EXISTS `telephones1`;
CREATE TABLE IF NOT EXISTS `telephones1`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL);
DROP INDEX IF EXISTS user_ids_and_telephones;
CREATE UNIQUE INDEX `user_ids_and_telephones` ON `telephones1`(`user_id`, `telephone`)
-- ON CONFLICT IGNORE commented out as is invalid
;
DROP TABLE IF EXISTS `telephones2`;
CREATE TABLE IF NOT EXISTS `telephones2`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL,
UNIQUE(`user_id`, `telephone`) ON CONFLICT IGNORE);
SELECT * FROM sqlite_master WHERE type = 'index' AND name LIKE '%telephones%';
INSERT INTO `telephones2` VALUES
(null,1,'phone1'),(null,2,'phone2'),(null,3,'phone1'),(null,1,'phone1');
INSERT INTO `telephones1` VALUES
(null,1,'phone1'),(null,2,'phone2'),(null,3,'phone1'),(null,1,'phone1');
The insert into telephones2 will not fail but only insert 3 of the 4 rows.
The
insert into telephones1 fails without inserting any rows.
as per :-
DROP TABLE IF EXISTS `telephones1`
> OK
> Time: 0.389s
CREATE TABLE IF NOT EXISTS `telephones1`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL)
> OK
> Time: 0.31s
DROP INDEX IF EXISTS user_ids_and_telephones
> OK
> Time: 0s
CREATE UNIQUE INDEX `user_ids_and_telephones` ON `telephones1`(`user_id`, `telephone`)
-- ON CONFLICT IGNORE
> OK
> Time: 0.366s
DROP TABLE IF EXISTS `telephones2`
> OK
> Time: 0.383s
CREATE TABLE IF NOT EXISTS `telephones2`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL,
UNIQUE(`user_id`, `telephone`) ON CONFLICT IGNORE)
> OK
> Time: 0.358s
SELECT * FROM sqlite_master WHERE type = 'index' AND name LIKE '%telephones%'
> OK
> Time: 0s
INSERT INTO `telephones2` VALUES
(null,1,'phone1'),(null,2,'phone2'),(null,3,'phone1'),(null,1,'phone1')
> Affected rows: 3
> Time: 0.356s
INSERT INTO `telephones1` VALUES
(null,1,'phone1'),(null,2,'phone2'),(null,3,'phone1'),(null,1,'phone1')
> UNIQUE constraint failed: telephones1.user_id, telephones1.telephone
> Time: 0.004s
As can be seen from the output of the query of the slqite_master two indexes are in fact created :-
The one attached to telephones2 being an automatically generated index (i.e. it starts with sqlite_autoindex)

Is it possible to insert the specified value in autoincrement column?

I need to copy one table to another and both tables contain column with AUTOINCREMENT. Is it possible to insert a defined value into AUTOINCREMENT column.
Tables:
CREATE TABLE tmptimetables (
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
_title NVARCHAR(256) NOT NULL,
_weeks INTEGER NOT NULL,
_first_week_date INTEGER NOT NULL,
_auto_complete INTEGER NOT NULL,
_first_lesson_time INTEGER NOT NULL,
_lesson_duration INTEGER NOT NULL,
_break_duration INTEGER NOT NULL,
_color INTEGER NOT NULL,
_symbol NCHAR(1) NOT NULL
);
CREATE TABLE timetables (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
title NVARCHAR(256) NOT NULL,
weeks INTEGER NOT NULL,
first_week_date INTEGER NOT NULL,
auto_complete INTEGER NOT NULL,
first_lesson_time INTEGER NOT NULL,
lesson_duration INTEGER NOT NULL,
break_duration INTEGER NOT NULL,
color INTEGER NOT NULL,
symbol NCHAR(1) NOT NULL
);
My SQL request:
INSERT INTO timetables (
auto_complete,
break_duration,
color,
first_lesson_time,
first_week_date, id,
lesson_duration,
symbol,
title,
weeks
)
SELECT
_auto_complete,
_break_duration,
_color,
_first_lesson_time,
_first_week_date,
_id,
_lesson_duration,
_symbol,
_title,
_weeks
FROM tmptimetables
AUTOINCREMENT can only be used for a column that is INTEGER PRIMARY KEY, it is INTEGER PRIMARY KEY that is the factor that makes the column a special column whereby if the value is not provided when inserting a row that a unique integer will be assigned.
So be the column INTEGER PRIMARY KEY or INTEGER PRIMARY KEY AUTOINCREMENT you can specify an integer value and a row may be inserted with the given value.
A row will not be inserted with a given value if that value is not unique.
For example if the table timetables is currently :-
Then
INSERT INTO timetables (id,title,weeks,first_week_date,auto_complete,first_lesson_time,lesson_duration,break_duration,color,symbol) VALUES (null,'mytitle',78,86000,23,1800,900,200,16,'E');
Would insert a new row with the id as determined by SQLite's algorithm for providing a unique id (probably 5).
If the id were changed to be provided (i.e. not null) say to 10 as per :-
INSERT INTO timetables (id,title,weeks,first_week_date,auto_complete,first_lesson_time,lesson_duration,break_duration,color,symbol) VALUES (10,'mytitle',78,86000,23,1800,900,200,16,'E');
Then the id for the new row would be 10.
However if then using (the same SQL but with the last column value changed) :-
INSERT INTO timetables (id,title,weeks,first_week_date,auto_complete,first_lesson_time,lesson_duration,break_duration,color,symbol) VALUES (10,'mytitle',78,86000,23,1800,900,200,16,'Z');
A new row would not be inserted as a row with an id of 10 already exists.
Finally if the id is not given (null is used) but the SQL is otherwise the same a new row is inserted with a unique id being provided by SQLite e.g.
INSERT INTO timetables (id,title,weeks,first_week_date,auto_complete,first_lesson_time,lesson_duration,break_duration,color,symbol) VALUES (null,'mytitle',78,86000,23,1800,900,200,16,'Z');
So the end result of following the above is :-
AUTOINCREMENT
The AUTOINCREMENT keyword, only usable for an INTEGER PRIMARY COLUMN, invokes a different algorithm for determining the next sequence to ensure that the next sequence/id is always greater, whilst without AUTOINCREMENT a lower sequence/id can be applied.
The AUTOINCREMENT keyword does not specify that if a value for the column is not provided then a sequence/id is applied it is INTEGER PRIMARY KEY that specifies that. Well actually, by default, i.e. unless WITHOUT ROWID is specified, this happens for all tables. It's just the the special rowid column is hidden. Specifying <column_name> INTEGER PRIMARY KEY (where is a valid column name) creates an alias of the rowid.
For example using SELECT rowid,* FROM timetables produces :-
SQLite Autoincrement
Rowid Tables

INSERT OR IGNORE INTO doesn't work

I have a table created as:
create table association (_id integer unique primary key autoincrement , id_rules integer, id_places integer)";
To avoid replication of entry, I use the statement INSERT OR IGNOR, but it doesn't work. For example,
value (id_rules , id_places) = ("11","1") alredy in table, but using:
INSERT OR IGNORE INTO association (id_rules , id_places) VALUES ("11","1")
a new row is created.
Please, do anyone Know hwere is my mistake?
INSERT OR IGNORE will ignore any rows that would violate a UNIQUE constraint.
The only such constraint is on the _id column, which you did not specify.
If you want to prevent duplicates in those two columns, you have to add a constraint for them to the table definition:
CREATE TABLE association (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
id_rules INTEGER,
id_places INTEGER,
UNIQUE (id_rules, id_places)
);

How do I update or insert an sqlite row with multiple conditions

I have three tables. A, B, and A_to_B. The relationship between A and B is many-to-many. This relationship information is stored in table A_to_B. It's construction is defined as follows:
CREATE TABLE
(id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
identifier_from_a TEXT NOT NULL,
identifier_from_b TEXT NOT NULL);
Each relationship is unique.
I would like to persist my relationship data with a single statement per relationship. My question is, how can I achieve this without inserting duplicates?
The solution is to use a multiple column UNIQUE definition in the create table statement.
For example:
CREATE TABLE
(id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
identifier_from_a TEXT NOT NULL,
identifier_from_b TEXT NOT NULL, UNIQUE (a,b) ON CONFLICT REPLACE);

How do I specify a Primary Key in Sqlite

How to define your specified attribute like StudentId in student table as Primary key in sqlite
CREATE TABLE Student(
id INTEGER PRIMARY KEY,
first_name TEXT,
last_name TEXT
);
From the Sqlite spec:
One exception to the typelessness of
SQLite is a column whose type is
INTEGER PRIMARY KEY. (And you must use
"INTEGER" not "INT". A column of type
INT PRIMARY KEY is typeless just like
any other.) INTEGER PRIMARY KEY
columns must contain a 32-bit signed
integer. Any attempt to insert
non-integer data will result in an
error.
http://www.sqlite.org/datatypes.html
You can also place a primary key on the arbitrary blobish data eg:
CREATE TABLE Student(id PRIMARY KEY, name)
Its a bit risky cause
INSERT INTO Student(1, "hello")
INSERT INTO Student("1", "hello")
will result in two rows.
If you need a unique constraint on other stuff you can try using the Create Index command
CREATE TABLE Students (
StudentId INTEGER PRIMARY KEY,
Name VARCHAR(80)
)
is one simple way.

Resources