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.
Related
I am using the DB Browser for SQLite to try and figure this out. I've opened Northwind.sqlite and in it it shows me the following for a table:
CREATE TABLE `Order Details` (
`OrderID` int,
`ProductID` int,
`UnitPrice` float ( 26 ),
`Quantity` int,
`Discount` float ( 13 ),
PRIMARY KEY(`OrderID`,`ProductID`)
);
However, in the Sql Server Northwind OrderID and ProductID are foreign keys, not primary keys. Does this work differently in SQLite? And if so, how do the relationships work?
thanks - dave
The above will create a table that has no FOREIGN keys but 2 indexes.
One a largely hidden index according to rowid.
The other, PRIMARY KEY(OrderID,ProductID) will be an index according to the combination of OrderId and ProductID.
some things about rowid (aka id)
rowid is an automatically created column called rowid (it can also be referenced using oid or rowid (case independent)) and if present is really the primary key.
rowid will be a unique signed integer using up to 64 bits. The lowest value and also the first value will be 1, the highest value being 9223372036854775807.
In later versions of SQLite 3.8.2 on the WITHOUT ROWID keyword was added to allow suppression of the rowid column/index (your Order Details table may benefit being a without rowid table).
if a column is defined with the type INTEGER PRIMARY KEY or INTEGER PRIMARY KEY AUTOINCREMENT then that column (there can only be 1 such column per table) is an alias of for the rowid column.
AUTOINCREMENT introduces a rule that when inserting a row the rowid must be greater than any that exist or existed.
It DOES NOT guarantee that the rowid will monotonically increase, although generally the id will (even without AUTOINCREMENT (perhaps the most misused/misunderstood keyword in SQLite)).
Without AUTOINCREMENT SQlite may find a lower rowid and use that, but not until a rowid of 9223372036854775807 has been reached.
AUTOINCREMENT, if a rowid of 9223372036854775807 has been reached will is an SQLITE_FULL exception.
AUTOINCREMENT results in overheads (e.q. a table named sqlite_sequence is then maintained recording the highest given sequence number). The documentation recommends that it not be used unless required, which is rarely the case.
Some limited testing I did resulted in an 8-12% greater processing time for AUTOINCREMENT. What are the overheads of using AUTOINCREMENT for SQLite on Android?
For more about rowid see SQLite Autoincrement and also Clustered Indexes and the WITHOUT ROWID Optimization
Coding PRIMARY KEY (if not on an INTEGER column i.e. not an alias of rowid) implies a UNIQUE constraint. It is not saying/checking that the value or any of the values in a clustered index exists in any other table.
Note null is not considered to be the same value, so in your Order Details table it is possible to have any combination of the values as null.
Coding a FOREIGN KEY introduces a constraint that the referenced value(s) must exist in the respective table/column. Additionally :-
Usually, the parent key of a foreign key constraint is the primary key
of the parent table. If they are not the primary key, then the parent
key columns must be collectively subject to a UNIQUE constraint or
have a UNIQUE index. If the parent key columns have a UNIQUE index,
then that index must use the collation sequences that are specified in
the CREATE TABLE statement for the parent table.
SQLite Foreign Key Support
Considering all of this you may want to do make some changes to the Order Details table :-
You could make it a WITHOUT ROWID table.
You could make both the OrderID and the ProductID columns NOT NULL.
You could add FOREIGN KEY's to both the OrderID and the ProductID columns.
So perhaps you could have :-
CREATE TABLE `Order Details` (
`OrderID` int NOT NULL REFERENCES `Orders` (`OrderId`), -- ADDED NOT NULL and FKEY
`ProductID` int NOT NULL REFERENCES `Products`(`ProductId`) , -- ADDED NOT NULL and FKEY
`UnitPrice` float ( 26 ),
`Quantity` int,
`Discount` float ( 13 ),
PRIMARY KEY(`OrderID`,`ProductID`)
)
WITHOUT ROWID -- ADDED WITHOUT ROWID
;
The above uses column constraints
Alternately, utilising TABLE constraints, you could do :-
CREATE TABLE `Order Details` (
`OrderID` int NOT NULL, -- ADDED NOT NULL
`ProductID` int NOT NULL, -- ADDED NOT NULL
`UnitPrice` float ( 26 ),
`Quantity` int,
`Discount` float ( 13 ),
PRIMARY KEY(`OrderID`,`ProductID`),
FOREIGN KEY (`OrderId`) REFERENCES `Orders`(`OrderId`), -- ADDED FKEY AS TABLE CONSTRAINT
FOREIGN KEY (`ProductID`) REFERENCES `Products`(`ProductID`) -- ADDED FKEY AS TABLE CONSTRAINT
)
WITHOUT ROWID -- ADDED WITHOUT ROWID
;
Both have the same outcome, the only difference being where the FOREIGN KEY constraints are defined.
Both the above assumes that the referenced tables are Orders and Products.
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.
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
I have table that has a TEXT primary key
CREATE TABLE tbl1{
a1 TEXT PRIMARY KEY,
...
);
(the a1 column is a foreign key inside another table)
How can I change values of a1?
If I do
UPDATE tbl1 SET a1 = ? WHERE a1 = ?
I get a constrain violation error
You should never change primary keys; it would be a better idea to use an INTEGER PRIMARY KEY and have the actual URL be a normal data column.
If you really want change a key that is the target of a foreign key, you should declare the foreign key constraint as deferred so that you are able to adjust the foreign key value in the same transaction.
The problem is that your table has single column that is the primary key and is a foreign key to another table. This suggests that the database design of the database is wrong.
Unless you can change the database structure you need to add the correct values in that other table to change your primary key value.
That is "insert into table constraintingTable(key,val) values (A,B)" and then execute update tbl set a1 = A where a1 = KEY.
Ignore the people telling you that primary keys should never be changed, there is a body of theory on how primary keys should be built. A primary key should uniquely identify the value columns of a row (see database theory), for instance typical keys are PNR, SSN, Serial Number, Mobile Phone number, and sometimes multi values like Name, Address, Street, Country. Generated keys should only be used if you generate new values or you have practical problems using a proper primary key.
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