SQLite Foreign Keys: Enable and Test Them
A shop database should not accept an order for a customer who does not exist. In SQLite, declaring a foreign key is only part of that protection: check that enforcement is enabled on the connection doing the work.
This example uses two small tables and invented records. Run it in a new, disposable SQLite database, not in your live customer database. Execute the statements in order; the two failure cases are intentional.
1. Enable enforcement, then read its state
PRAGMA foreign_keys = ON;
PRAGMA foreign_keys;
The second statement should return 1. The assignment turns enforcement on; the separate query checks it. Set this when each connection opens, before starting a transaction. Changing it inside an active transaction has no effect. Do not assume your framework or another connection has enabled it for you.
2. Create a customer and an order
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL
REFERENCES customers(id) ON DELETE RESTRICT
);
INSERT INTO customers VALUES (1, 'Demo customer');
INSERT INTO orders VALUES (101, 1);
SELECT id, customer_id FROM orders;
The query returns order 101 with customer 1. The reference points to the customer's primary key. NOT NULL also matters: this shop requires every order to have a customer.
3. Prove that an orphan row is rejected
INSERT INTO orders VALUES (102, 999);
There is no customer 999. Expect a foreign-key constraint error, not a second order. Your SQLite tool may display an error code as well as the message. Run the SELECT again: order 101 should still be the only row.
Also try this statement by itself:
DELETE FROM customers WHERE id = 1;
It should fail because order 101 still refers to that customer. We chose ON DELETE RESTRICT deliberately. A cascade would have different consequences, so do not add it simply to make a deletion succeed. Decide how your application should preserve order history first.
4. Check existing data separately
PRAGMA foreign_key_check;
In the clean example it returns no rows. Turning enforcement on does not repair violations already stored while it was off. On a copy of an older database, this check reports the affected child table, its rowid, the parent table and the foreign-key identifier.
For example, an orders row with id 103 pointing to missing customer 999 would produce orders | 103 | customers | 0 in this schema. Investigate the original record before deciding whether to correct the reference, restore the legitimate parent or remove an invalid test record.
If the bad insert unexpectedly succeeds
- Query
PRAGMA foreign_keys;on the same connection that made the insert. - Check whether you attempted to enable enforcement after a transaction had already started.
- Inspect the actual schema with
PRAGMA foreign_key_list(orders);. A column name alone does not declare a relationship. - Confirm that your connection opened the intended database file.
The useful test has three parts: a valid order succeeds, an orphan order fails, and the consistency check is empty. Keep these checks in your application's database tests so a new connection setup does not silently remove the protection.
Official references: SQLite foreign-key support and PRAGMA foreign_key_check.
Worked SQL example checked with Python's SQLite library on 10 October 2026. Error formatting can differ between SQLite clients.