Foreign key in main database referencing attached database - sqlite

Is there any way in SQLite3 to have a foreign key in the main database that references columns in an attached database (or vice-versa?)
I hope to share the attached (read-only) database between multiple processes, each of which has its own (read/write) main database.
I create the parent table like this (in database 'ParentDB'):
create table Parent (id integer primary key);
Now I try this in the main database:
attach 'parent.sqlite3' as ParentDB;
create table Child (id integer not null references Parent (id),
constraint PK_Child primary key (id));
insert into ParentDB.Parent (id) values (42);
When I try it it creates the foreign key with no errors. Now I try to insert a row into the child table:
insert into Child (id) values (42);
And I get this error:
Error: no such table: main.Parent
So it seems it always assumes the parent and child tables belong to the same database.
Also the foreign key syntax does not allow you to specify which database the parent table belongs to.
Is there a workaround?
This question is related, but here both the parent and child tables are in the same attached database, whereas I have them in separate databases.

SQLite's built-in foreign key constraints do not work across databases.
The only workaround would be to manually write check constraints and triggers that do the same checking.

Related

sqlite- interlinking two database tables using foreign keys

In SQL in general, but in SQLite for this case, can we establish a foreign key from one table say, table in db1.db to mainTable in db2.db. That is we can use a foreign key relationship in table by referencing the primary key in mainTable, the main idea being to use this relationship to trigger ON DELETE CASCADE. (Briefly in my case, mainTable happens to be a system maintained SQLITE database, which I'd like to avoid manual finding and analyzing. If I could somehow access establish this key, life would be a lot easier).

What if a table doesn't have a primary key

I have made a simple relation table. All consist of three tables:
Tables for storing personal data (Table_Person)
Table for storing address data (Table_Address)
Table to store the relationship between Table_Person and Table_Address (Table_PersonAddress).
What I want to ask is can I delete the primary key in Table_PersonAddress so that Table_PersonAddress doesn't have a primary key and all that's left is the personID and addressID?
Below is an example of a database relation that I made:
enter image description here
Assuming you don't have any foreign key constraints setup on the junction table (that is, the third table which just stores relationships between people and their addresses), you could delete a person from the first table, while leaving behind the relationships in the third table. However, just because you could do this, does not mean you would want to. Most of the time, if you remove a person from the first table, you would also want to remove all of his relationships from the third table. One way to do this in SQLite is by adding cascading delete constraints to the third table, when you create it:
CREATE TABLE Table_PersonAddress (
...
CONSTRAINT fk_person
FOREIGN KEY (personID)
REFERENCES Table_Person (ID)
ON DELETE CASCADE
)
You probably would also want to add a similar constraint for the address field in the third table, since removing an address also invalidates all relationships involving that address.
Note that SQLite does not allow a cascading delete constraint to be added to table which already exists. You will have to recreate your tables somehow in order to add these constrains.
You can delete it, but my advice is to set a composite PRIMARY KEY for the 2 columns personID and addressID so each row is guaranteed to be UNIQUE.
PRIMARY KEY (personID, addressID)
and remember that in SQLite you always have the rowid column to use it as an id of the row if needed.
So create the table with this statement:
DROP TABLE IF EXISTS PersonAddress;
CREATE TABLE PersonAddress (
personID INTEGER,
addressID INTEGER,
PRIMARY KEY(personID, addressID),
FOREIGN KEY (personID) REFERENCES Person (personID) ON DELETE CASCADE,
FOREIGN KEY (addressID) REFERENCES Address (addressID) ON DELETE CASCADE
);
One more thing: why did you define personID and addressID as TEXT?
Surely SQLite is not at all strict at data type definitions, but since the columns they reference are INTEGER they also should be INTEGER.

Create Table with deferred foreign key referencing each other

I'm new to sqlite and sql in gerneral so I don't know if my approach is reasonable.
I want to model inventory items that can be created, lent, returned and discarded.
I want to model this using two tables, one for items, containing an id, a name and a reference to the last transaction (created, lent, returned, ...) and a table of transactions containing an id transaction type, date, and a reference to the item.
Since creating only one table leaves the database in an inconsitent state with one table referencing a non existant table I thought of using a transaction to crate both tables at once, and defining the foreign keys as deferrable. Creation of a new item would have to be done together in one transaction with creating a "created" event to leave the database in a consistent state.
The following code gives me the error Query Error: not an error Unable to execute multiple statements at a time in sqliteman on linux.
PRAGMA foreign_keys = ON;
begin transaction;
create table items (
id integer primary key,
name char(30),
foreign key (last_transaction) references transactions(transaction_id) DEFERRABLE INITIALLY DEFERRED
);
create table transactions(
transaction_id integer primary key,
text char(100)
foreign key (item) references items(id) DEFERRABLE INITIALLY DEFERRED
);
commit transaction;
Does my approach make sense at all?
If yes, why does the code not work? (Did I make a mistake somewhere, or is what I'm trying impossible in mysql?)
Note: simply creating the tables in one transaction without the foreign key constraints gives the same error. (Could this be a similar Problem to: this question)

SQLite: Delete Rule Capabilities With Reflexive Joins?

I am attempting to use foreign key support in SQLite to maintain referential integrity on a single-table database that has a reflexive join.
e.g.
PRAGMA foreign_keys = ON;
create table tree (
objectId text unique not null,
parentObjectID text,
foreign key (parentObjectID) references tree(parentObjectID) on delete cascade
)
The behavior that I am hoping for is that when a parent row is deleted, its children and their children are deleted as well.
However, when I attempt to delete the root row (where the expected behavior would be that every other row in the database is also deleted), I get this error:
sqlite> delete from tree where objectid = '0';
Error: foreign key mismatch
Are my expectations out of whack with with SQLite foreign key support (and delete behaviors) can provide?
Your problem is pretty simple, your FK on parentObjectId references parentObjectId rather than objectId and SQLite doesn't detect this bit of confusion until you try to use the table. If your FK is defined like this:
foreign key (parentObjectID) references tree(objectID) on delete cascade
From the fine manual:
So, in other words, misconfigured foreign key constraints that require looking at both the child and parent are DML errors. The English language error message for foreign key DML errors is usually "foreign key mismatch" but can also be "no such table" if the parent table does not exist. Foreign key DML errors are may be reported if:
The parent table does not exist, or
The parent key columns named in the foreign key constraint do not exist, or
The parent key columns named in the foreign key constraint are not the primary key of the parent table and are not subject to a unique
constraint using collating sequence specified in the CREATE TABLE, or
The child table references the primary key of the parent without specifying the primary key columns and the number of primary key
columns in the parent do not match the number of child key columns.
The third point would seem to apply here since parentObjectId is neither a PK nor constrained to be unique so that's why you don't see an error until you try to modify the table's content (i.e. use a DML statement rather than a DDL statement).

Does a SQLite Foreign key automatically have an index?

I know that SQLite does not enforce foreign keys natively, but that's not my primary concern. The question is: If I declare
CREATE TABLE invoice (
invoiceID INTEGER PRIMARY KEY,
clientID INTEGER REFERENCES client(clientID),
...
)
will sqlite at least use the information that clientID is a foreign key to optimize queries and automatically index invoice.clientID, or is this constraint a real no-op?
In the SQLite Documentation it says:
... "an index should be created on the child key columns of each foreign key constraint"
ie. the index is not automatically created, but you should create one in every instance.
Even if it is not actually a no-op (a data structure describing the constraint is added to the table), foreign key related statement doesn't create any index on involved columns.
Indexes are implicitly created only in the case of PRIMARY KEY and UNIQUE statements.
For more details, check it out build.c module on the sqlite source tree:
http://www.sqlite.org/cvstrac/rlog?f=sqlite/src/build.c https://www.sqlite.org/src/file?name=src/build.c&ci=tip

Resources