SQL Foreign Keys and Relationships — Connecting Tables in Relational Databases
Foreign Keys
An exam result should not claim to belong to a student whose roll number does not exist.
A foreign key restricts child key values to permitted references in a parent relation, subject to the schema’s nullability and configured relationship actions.
Explain and apply
Connect result rows to valid students and predict what a foreign key rejects.
Work it out
Identify the deliberately orphaned result in the practice fixture, before designing an enforced relationship.
Avoid mistakes
Nullable child keys can permit missing relationships unless NOT NULL is required.
Foreign Keys: the request and its result
School need
An exam result should not claim to belong to a student whose roll number does not exist.
Database operation
A foreign key restricts child key values to permitted references in a parent relation, subject to the schema’s nullability and configured relationship actions.
Check the outcome
The sample results relation intentionally has no enforced foreign key so unmatched join behavior can be learned. An appropriate enforced, non-null parent reference would reject that orphan insertion.
Understand what the database does
The child stores the parent key. The database checks the relationship when relevant writes occur. ON DELETE and ON UPDATE actions specify whether parent changes are rejected, cascaded or handled in another supported way. A many-to-many relationship uses a junction table with references to both parents.
Related concepts: LEFT JOIN and Data Constraints and Transactions.
What is a Foreign Key?
A foreign key is a column (or set of columns) in one table that refers to the primary key of another table. It establishes a link between two tables and enforces referential integrity — meaning you can't insert unrelated or orphaned data.
Basic School Example
SQL · portable concepts; preserved examples identify their engine
1. Create students table
CREATE TABLE students (
roll_no INT PRIMARY KEY,
name VARCHAR(50),
class VARCHAR(10)
);
2. Create marks table with a foreign key
CREATE TABLE marks (
id INT AUTO_INCREMENT PRIMARY KEY,
roll_no INT,
subject VARCHAR(30),
marks INT,
FOREIGN KEY (roll_no) REFERENCES students(roll_no)
);
This means every row in marks must match an existing roll_no in students.
Inserting Valid and Invalid Data
SQL · portable concepts; preserved examples identify their engine
Valid insert:
INSERT INTO students VALUES (1, 'Aarav Sharma', '10A');
INSERT INTO marks (roll_no, subject, marks) VALUES (1, 'Maths', 85);
Invalid insert:
-- Error: roll_no 5 doesn't exist in students table
INSERT INTO marks (roll_no, subject, marks) VALUES (5, 'Science', 90);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails
Types of Relationships Using Foreign Keys
SQL · portable concepts; preserved examples identify their engine
1. One-to-Many (Most Common)
A student can have many marks entries, but each mark entry belongs to one student.
2. Many-to-One
Multiple students can belong to the same class or be assigned to the same teacher.
3. Many-to-Many (via Junction Table)
Students can enroll in many subjects, and subjects can have many students.
CREATE TABLE enrollments (
roll_no INT,
subject_code VARCHAR(10),
PRIMARY KEY (roll_no, subject_code),
FOREIGN KEY (roll_no) REFERENCES students(roll_no),
FOREIGN KEY (subject_code) REFERENCES subjects(subject_code)
);
ON DELETE and ON UPDATE Options
SQL · portable concepts; preserved examples identify their engine
Foreign keys can respond to changes in the parent table using:
ON DELETE CASCADE: Deletes dependent rows if parent is deletedON DELETE SET NULL: Sets FK to NULL if parent is deletedON DELETE RESTRICT: Prevents deletion if child rows exist
CREATE TABLE marks (
id INT AUTO_INCREMENT PRIMARY KEY,
roll_no INT,
subject VARCHAR(30),
marks INT,
FOREIGN KEY (roll_no) REFERENCES students(roll_no)
ON DELETE CASCADE
);
Real-World School Use Cases
- students → marks: Track subject-wise performance
- students → attendance: Link attendance records to students
- subjects → teachers: Assign subjects to teachers
- students ↔ subjects: Use a join table for student enrollments
Best Practices
Query discussed in this section:
CREATE TABLE marks (
id INT AUTO_INCREMENT PRIMARY KEY,
roll_no INT,
subject VARCHAR(30),
marks INT,
FOREIGN KEY (roll_no) REFERENCES students(roll_no)
ON DELETE CASCADE
);
- Always index foreign key columns for better performance
- Name constraints explicitly if needed (for debugging)
- Use
CASCADEonly when deletions should flow down - Plan your relationships before schema creation (ER diagrams help!)
Important differences and mistakes
Nullable child keys can permit missing relationships unless NOT NULL is required. Cascading deletion has a real scope and should be chosen intentionally. SQLite requires foreign-key enforcement to be enabled per connection in typical setups; MySQL enforcement depends on a supporting engine such as InnoDB.
Try it with the school data
Identify the deliberately orphaned result in the practice fixture, before designing an enforced relationship.
This practice uses a separate, fresh fixture named practice_students. It does not continue the changing table state of the earlier examples.
| roll_no | name | class | marks | city |
|---|---|---|---|---|
| 1 | Aarav | 10A | 85 | Pune |
| 2 | Diya | 9B | 70 | Chennai |
| 3 | Sneha | 10A | 85 | Pune |
| roll_no | subject | marks |
|---|---|---|
| 1 | Maths | 85 |
| 3 | Maths | 85 |
| 4 | Maths | 92 |
SQL · create only these practice tables in a disposable database:
CREATE TABLE practice_students (roll_no INTEGER PRIMARY KEY, name VARCHAR(50), class VARCHAR(10), marks INTEGER, city VARCHAR(30));
INSERT INTO practice_students VALUES
(1, 'Aarav', '10A', 85, 'Pune'),
(2, 'Diya', '9B', 70, 'Chennai'),
(3, 'Sneha', '10A', 85, 'Pune');
CREATE TABLE practice_results (roll_no INTEGER, subject VARCHAR(30), marks INTEGER);
INSERT INTO practice_results VALUES (1,'Maths',85),(3,'Maths',85),(4,'Maths',92);
Worked answer and reasoning
This practice uses a separate, fresh fixture named practice_students. It does not continue the changing table state of the earlier examples.
| roll_no | name | class | marks | city |
|---|---|---|---|---|
| 1 | Aarav | 10A | 85 | Pune |
| 2 | Diya | 9B | 70 | Chennai |
| 3 | Sneha | 10A | 85 | Pune |
| roll_no | subject | marks |
|---|---|---|
| 1 | Maths | 85 |
| 3 | Maths | 85 |
| 4 | Maths | 92 |
SQL · create only these practice tables in a disposable database:
CREATE TABLE practice_students (roll_no INTEGER PRIMARY KEY, name VARCHAR(50), class VARCHAR(10), marks INTEGER, city VARCHAR(30));
INSERT INTO practice_students VALUES
(1, 'Aarav', '10A', 85, 'Pune'),
(2, 'Diya', '9B', 70, 'Chennai'),
(3, 'Sneha', '10A', 85, 'Pune');
CREATE TABLE practice_results (roll_no INTEGER, subject VARCHAR(30), marks INTEGER);
INSERT INTO practice_results VALUES (1,'Maths',85),(3,'Maths',85),(4,'Maths',92);
SQL · this practice variation runs with SQLite 3; the preserved examples retain their stated dialect
SELECT r.roll_no FROM practice_results AS r LEFT JOIN practice_students AS s ON s.roll_no = r.roll_no WHERE s.roll_no IS NULL ORDER BY r.roll_no;| roll_no |
|---|
| 4 |
The sample results relation intentionally has no enforced foreign key so unmatched join behavior can be learned. An appropriate enforced, non-null parent reference would reject that orphan insertion.
Check your understanding
Use the idea independently
A foreign key restricts child key values to permitted references in a parent relation, subject to the schema’s nullability and configured relationship actions.
The sample results relation intentionally has no enforced foreign key so unmatched join behavior can be learned. An appropriate enforced, non-null parent reference would reject that orphan insertion.
Continue your understanding with LEFT JOIN and Data Constraints and Transactions.
Source for the engine-specific behavior: MySQL 8.4 reference documentation.
Reaching the recap and completing the quizzes are separate milestones.