Combination of two columns unique constraint - sqlite

I created the table t1t2 which connects tables t1 and t2 as follows:
CREATE TABLE t1t2(
id integer primary key,
t1_id integer,
t2_id integer,
foreign key(t1_id) references t1(id),
foreign key(t2_id) references t2(id));
Is it possible to define a constraint (restriction) that enables only unique values of tuple (t1_id, t2_id)? Or should I check this in the application?

CREATE UNIQUE INDEX idx_twocols ON t1t2(t1_id, t2_id)
You will probably need to add NOT NULL to the declarations for each of the two columns.
Alternatively, you could choose to forego the primary key column (if all you're using it for is uniqueness) and create the primary key on the combination of t1_id and t2_id:
CREATE TABLE t1t2(
t1_id integer NOT NULL,
t2_id integer NOT NULL,
PRIMARY KEY (t1_id, t2_id),
foreign key(t1_id) references t1(id),
foreign key(t2_id) references t2(id));
The PRIMARY KEY is a special case of a UNIQUE index. Using the composite PRIMARY KEY saves you one column and one index, but requires your application to know both t1_id and t2_id to retrieve a single row from the table.

You can add a unique constraint to your create table statement.
This does not have to be the primary key.
UNIQUE(t1_id, t2_id),

You could create your UNIQUE primary index with those options to keep your primary key and a unique constraint SQL Lite New Index option

Related

Do I need to create index for PK in SQLite tables?

If I have a single column integer PK on a SQLite table - do I need to create explicit index or is it take care of by the engine?
Same question for string/text single field PK
From UNIQUE constraints:
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.)
If a column is defined as INTEGER PRIMARY KEY it is actually an alias of the rowid of the column:
The data for rowid tables is stored as a B-Tree structure containing
one entry for each table row, using the rowid value as the key. This
means that retrieving or sorting records by rowid is fast. Searching
for a record with a specific rowid, or for all records with rowids
within a specified range is around twice as fast as a similar search
made by specifying any other PRIMARY KEY or indexed value.
The exception mentioned above is that if the declaration of a column with
declared type "INTEGER" includes an "PRIMARY KEY DESC" clause, it does
not become an alias for the rowid and is not classified as an integer
primary key.

What happens if you declare an SQLite column PRIMARY KEY + UNIQUE?

SQLite's documentation says:
A UNIQUE constraint is similar to a PRIMARY KEY constraint, except that a single table may have any number of UNIQUE constraints.
What I'm wondering is, if I declare something like:
CREATE TABLE Example (
id INTEGER PRIMARY KEY UNIQUE);
Does SQLite create two indexes or one? Is the behavior different if I'm not using the rowid (i.e. if the column was id TEXT PRIMARY KEY UNIQUE)?
I realize the simplest thing to do is just remove UNIQUE but I'm curious what effect this will have.
When you define a primary key it will be unique, no need to define another index for unique column.
The point of primary keys is so that they are unique for any given row.
Meaning you can choose one or more fields to be a primary key, but a single field doesn't have to be unique.
Unique on the other hand has to be unique for the specified fields.
So it's more like a constraint you are placing.
Given this example:
CREATE TABLE IF NOT EXISTS user2domain (
userID INTEGER NOT NULL,
domainID INTEGER NOT NULL,
PRIMARY KEY (userID, domainID),
UNIQUE(userID, domainID) ON CONFLICT IGNORE
)
You don't actually have to specify the unique constraint as the primary key would cover the same fields.
CREATE TABLE IF NOT EXISTS user2domain (
userID INTEGER NOT NULL,
domainID INTEGER NOT NULL,
PRIMARY KEY (userID, domainID)
)
-- add this way
INSERT OR IGNORE INTO user2domain(userID, domainID) VALUES(#userID, #domainID)
This would be enough.
Use the Unique constraint when you absolutely feel like any fields
must be unique but not necessarily a row identifier.
The expected behaviour is overwriting. Primary is of a higher order precedence.

Data first many to many for EF Core

I have a SQLite DB that I am trying to use with EF Core database first.
It has a table of users, and a table of groups that users can belong to, and it has a mapping table because users can belong to multiple groups.
-- holds users
CREATE TABLE IF NOT EXISTS user (
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT,
);
-- holds groups users can belong to
CREATE TABLE IF NOT EXISTS group (
_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT,
);
-- holds user group membership
CREATE TABLE IF NOT EXISTS map_group_user (
group_id INTEGER,
user_id INTEGER,
UNIQUE (group_id,user_id) ON CONFLICT REPLACE,
FOREIGN KEY(group_id) REFERENCES group(_id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES user(_id) ON DELETE CASCADE
);
When I scaffold this up I get a warning from dotnet ef scaffold that it could not identify a primary key for map_group_user and it does not generate a model, and neither the User nor Group model contains any reference to the other (expected).
Try adding an explicit primary key to the map_group_user bridge table:
CREATE TABLE IF NOT EXISTS map_group_user (
group_id INTEGER,
user_id INTEGER,
UNIQUE (group_id,user_id) ON CONFLICT REPLACE,
FOREIGN KEY(group_id) REFERENCES "group"(_id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES user(_id) ON DELETE CASCADE,
PRIMARY KEY (group_id, user_id)
);
The logical primary key for the map_group_user table is the combination of group_id and user_id, each combination which should ideally appear only once.
By the way, please avoid naming your tables and columns using reserved SQL keywords, such as group. I don't know if this was giving you an error, but I have placed "group" in double quotes to escape it.

Am I defining all my keys correctly? Do I need to add an index?

I have a couple tables: Accounts and Employees, and I am trying to store a Rating and Timestamp for any given combination between Accounts and Employees. It should not be possible for two ratings to exist for the same timestamp for a given Account + Employee combination.
This is what I have so far:
CREATE TABLE main_table (
_ID INTEGER PRIMARY KEY AUTOINCREMENT,
account_id INTEGER NOT NULL,
employee_id INTEGER NOT NULL,
rating REAL NOT NULL,
timestamp LONG NOT NULL,
FOREIGN KEY(account_id) REFERENCES ACCOUNTS(_ID),
FOREIGN KEY(employee_id) REFERENCES EMPLOYEES(_ID),
UNIQUE (account_id, employee_id, timestamp));
Is this the correct way to define what I am trying to do? Do I also need to create a separate index?
CREATE INDEX main_table_idx ON main_table (account_id, employee_id, timestamp);
Unique constraints are implemented using a unique index.
Hence, separately creating the index is not necessary.
As explained in the documentation:
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.)
Based on your description, the unique constraint seems correct.

Can someone give me a PK insert sample?

So I'm making things complicated ...I think. A primary key basically is to make the row unique. Is that correct? Anyone want to show me an insert statement with the values for PK?
The SQLite documentation says:
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.
So, on a table like
CREATE TABLE test(id INTEGER PRIMARY KEY, descr TEXT);
an insert with a valid id could be
INSERT INTO test(descr) VALUES('this is a test');
A primary key, also called a primary keyword, is a key in a relational database that is unique for each record. It is a unique identifier, such as a driver license number, telephone number (including area code), or vehicle identification number (VIN). A relational database must always have one and only one primary key.
if you are using CREATE TABLE, if you are creating the primary key on a single field, you can use:
CREATE TABLE mytable (
field1 TEXT,
field2 INTEGER PRIMARY KEY,
field3 BLOB,
);
Reference more at: https://www.sqlite.org/lang_createtable.html & http://sqlite.org/faq.html#q11

Resources