Q1You wrote REFERENCES category(cat_id) on a column, yet a row pointing to a non-existent category went in. What's the most likely cause?
Foreign Key Constraints and Referential Actions (ON DELETE / ON UPDATE)
With a category–item parent/child setup, learn REFERENCES for referential integrity, why PRAGMA foreign_keys=ON is required to enable checks, and how ON DELETE CASCADE / SET NULL / RESTRICT each propagate parent deletes — all hands-on.
The data we'll use — category and item
A foreign key constraint (the constraint that guarantees a column's value always exists as a primary key in another table) is the mechanism that protects referential integrity between tables (the state where every referenced row actually exists).
It blocks contradictions like "an item points to a non-existent category" or "deleting a category leaves its items dangling," at the database layer.
Foreign-key checking is enabled with PRAGMA foreign_keys=ON
Foreign key constraints are declared by writing REFERENCES parent_table(parent_col) in the child table's column definition.
item.cat_id carries REFERENCES category(cat_id), which encodes the rule: every item.cat_id value must exist in category.
However, in this course's console the foreign-key check defaults to off (PRAGMA foreign_keys is 0).
While it's off, `REFERENCES` is not actually enforced and rows pointing to a non-existent category can be inserted.
To actually enforce the constraint, run PRAGMA foreign_keys=ON; once per connection.
-- Enable foreign-key checking
PRAGMA foreign_keys=ON;
-- Confirm the state (1 means enabled)
PRAGMA foreign_keys;
-- Try to insert a row pointing to cat_id=99 (not in category)
-- With foreign_keys=ON, this is rejected with FOREIGN KEY constraint failed
INSERT INTO item VALUES (5,'Mouse',99);
ON DELETE — what to do with child rows when a parent row is deleted
When a parent (category) row is deleted, what should happen to the child (item) rows that pointed to it? You decide by writing a referential action in the REFERENCES clause.
There are three common referential actions.
ON DELETE CASCADE means deleting the parent also deletes the children (cascade delete), ON DELETE SET NULL means the child's reference column is set to NULL and the row is kept, and ON DELETE RESTRICT means the parent delete itself is refused while children still reference it.
If you don't specify an action, the default is also RESTRICT-like (a referenced parent cannot be deleted).
Which behavior is appropriate depends on the business requirement.
| Referential action | When the parent category is deleted | What happens to the child item |
|---|---|---|
| ON DELETE CASCADE | Deleted | Children are cascade-deleted along with it |
| ON DELETE SET NULL | Deleted | Children remain, cat_id becomes NULL |
| ON DELETE RESTRICT | Cannot delete — errors | Parent delete is refused while children exist (untouched) |
-- Example declaration with ON DELETE SET NULL
CREATE TABLE cat_demo(cat_id INTEGER PRIMARY KEY, cat_name TEXT NOT NULL);
CREATE TABLE item_demo(
item_id INTEGER PRIMARY KEY,
item_name TEXT NOT NULL,
cat_id INTEGER REFERENCES cat_demo(cat_id) ON DELETE SET NULL
);
INSERT INTO cat_demo VALUES (1,'Stationery'),(2,'Electronics');
INSERT INTO item_demo VALUES (1,'Pen',1),(2,'Phone',2),(3,'Cable',2);
-- Deleting parent cat_id=2 turns Phone / Cable's cat_id to NULL
DELETE FROM cat_demo WHERE cat_id=2;
SELECT item_id, item_name, cat_id FROM item_demo ORDER BY item_id;
SET NULL and RESTRICT — choose between keeping and refusing
ON DELETE SET NULL keeps the child row when the parent is deleted, just replacing the referencing column with NULL.
Use it when you don't want to delete the child record itself, like "keep the item as 'uncategorized.'"
If the child's reference column is NOT NULL, NULL can't be written, so columns used with SET NULL must allow NULL.
ON DELETE RESTRICT refuses the parent delete itself while children still reference it.
Use it when you want to fail safe (don't accidentally drop a master row that's still referenced).
Once you've deleted or reassigned all the children so no references remain, the parent can be deleted.
In the next exercise you'll set up SET NULL and RESTRICT side by side in one console to compare them.
-- RESTRICT example: parent with children can't be deleted
CREATE TABLE cat_r(cat_id INTEGER PRIMARY KEY, cat_name TEXT NOT NULL);
CREATE TABLE item_r(
item_id INTEGER PRIMARY KEY,
item_name TEXT NOT NULL,
cat_id INTEGER REFERENCES cat_r(cat_id) ON DELETE RESTRICT
);
INSERT INTO cat_r VALUES (1,'Stationery'),(2,'Electronics');
INSERT INTO item_r VALUES (1,'Pen',1);
-- cat_id=1 is referenced by item_r, so deletion is refused
DELETE FROM cat_r WHERE cat_id=1;
-- cat_id=2 has no references, so it can be deleted
DELETE FROM cat_r WHERE cat_id=2;
SELECT * FROM cat_r ORDER BY cat_id;
Knowledge Check
Answer each question one by one.
Q2Which referential action causes a child row to be automatically deleted when its parent row is deleted?
Q3What condition must the child table's reference column meet to use ON DELETE SET NULL?