Primary Key vs Foreign Key in SQL: The Difference Explained
On this page 9 sections ▾
A primary key identifies a row inside its own table. A foreign key points at a row in another table. That is the whole difference, and every other distinction — how many you can have, whether NULL is allowed, what happens on delete — follows from it.
The two work as a pair: the foreign key is only meaningful because the primary key it points at is guaranteed unique. This page shows the pair in action, then covers the design decisions that come up in interviews and in real schemas.
The difference in one table
| PRIMARY KEY | FOREIGN KEY | |
|---|---|---|
| Purpose | identifies this row | references a row elsewhere |
| Values must be unique | yes | no - many rows may point at the same parent |
| NULL allowed | never | yes, unless also NOT NULL |
| How many per table | exactly one | as many as needed |
| Creates an index | automatically | usually not - add one yourself |
| Guarantees | no duplicates, no missing identity | the referenced row exists |
| Can span several columns | yes (composite key) | yes, matching the parent key |
The row worth dwelling on is the index one, because it is the most common real-world performance mistake. A primary key gets an index for free. A foreign key generally does not, and a join or a cascading delete on an unindexed foreign key is slow for exactly that reason. See how indexes work.
The pair in action
Two tables: cities, and offices that must be in a city that exists:
CREATE TABLE Cities (
city_id INT PRIMARY KEY,
name STRING NOT NULL
);
CREATE TABLE Offices (
office_id INT PRIMARY KEY,
city_id INT NOT NULL,
FOREIGN KEY (city_id) REFERENCES Cities (city_id)
);
INSERT INTO Cities VALUES (1, 'Pune');
INSERT INTO Offices VALUES (10, 1);
SELECT o.office_id, c.name AS city
FROM Offices o
JOIN Cities c ON c.city_id = o.city_id;
| office_id | city |
|---|---|
| 10 | Pune |
● 1 row · produced by running this query on the sample database
Now try inserting an office in city 99. The engine refuses it: Foreign key "99" not found in table "Cities". Without that FOREIGN KEY line the column would accept 99 happily, and you would have an office in a city that does not exist — a row no join will ever return, which is how orphaned data quietly accumulates.
The sample database on this site is built the same way. Orders.customer_id points at Customers.customer_id, which is what makes this join possible at all:
SELECT o.order_id, c.company, o.order_total
FROM Orders o
JOIN Customers c ON c.customer_id = o.customer_id
ORDER BY o.order_total DESC
LIMIT 5;
| order_id | company | order_total |
|---|---|---|
| 5001 | Meridian Trading Co. | 15588 |
| 5015 | Cedar Health | 9093 |
| 5010 | Fjord Analytics | 8091 |
| 5007 | Acme Robotics | 6495 |
| 5013 | Fjord Analytics | 5800 |
● 5 rows · produced by running this query on the sample database
Choosing a primary key
The single most consequential schema decision, and the one interviewers probe with “could you use the email as the primary key?”. You can. You usually should not.
- It has to be unique — forever. Names are not. Phone numbers get reassigned. Even “national id + date of birth” has collisions in real datasets.
- It should never change. This is the big one. Change a primary key and every foreign key pointing at it has to change in the same transaction. People change their email addresses; that is a data update, not a schema migration.
- It should be small. Every index on the table and every foreign key elsewhere stores a copy of it. A 200-character text key is copied everywhere.
- It must never be NULL. That is enforced, not advice.
Which is why most schemas use a surrogate key — an integer or UUID that means nothing outside the database — and put a UNIQUE constraint on the email instead. You get both guarantees, and the identity is immune to the real world changing its mind. A natural key (a real attribute like a country code) is defensible when the value genuinely never changes and is genuinely unique, which is rarer than it sounds.
Composite keys
A primary key can be several columns together, which is the natural fit for a table that records a relationship between two things:
CREATE TABLE Enrolments (
student_id INT,
course_id INT,
grade STRING,
PRIMARY KEY (student_id, course_id)
);
INSERT INTO Enrolments VALUES (1, 100, 'A'), (1, 101, 'B');
SELECT * FROM Enrolments;
| student_id | course_id | grade |
|---|---|---|
| 1 | 100 | A |
| 1 | 101 | B |
● 2 rows · produced by running this query on the sample database
Student 1 can be on two courses, and a course can have many students, but the same student cannot be enrolled on the same course twice — the combination is what must be unique. Neither column alone is a key; the pair is.
A foreign key can be composite too, and then it must match the parent key column for column, in order. Composite keys are correct here and awkward everywhere else: every child table has to carry both columns, so many teams add a surrogate enrolment_id and keep a UNIQUE (student_id, course_id) constraint alongside it. Both designs are defensible; be able to say why you chose yours.
What happens when the parent row is deleted?
This is the part of foreign keys that changes behaviour rather than just refusing things. The referential action decides what the database does to the children:
| Action | When the parent row is deleted | Use when |
|---|---|---|
| NO ACTION / RESTRICT (default) | the delete is refused | children must never be orphaned - the safe default |
| CASCADE | the children are deleted too | a child cannot exist alone: order lines with their order |
| SET NULL | the children keep existing, pointing at nothing | the link is optional: an order whose employee left |
| SET DEFAULT | the children point at a default row | rare - needs a sensible default to exist |
CREATE TABLE OrderLines (
line_id INT PRIMARY KEY,
order_id INT NOT NULL,
FOREIGN KEY (order_id) REFERENCES Orders (order_id) ON DELETE CASCADE
);
CASCADE deletes more than people expect
It is silent, and it chains: deleting a customer can remove their orders, and the order lines under those orders, in one statement that mentioned none of them. Use it where the child genuinely has no independent existence, and reach for the default RESTRICT everywhere else — a refused delete is a conversation, a cascaded one is a recovery.
ON UPDATE takes the same options and matters far less, precisely because a well-chosen primary key never changes.
Common misconceptions
- “A foreign key must reference a primary key.” It must reference a column with a unique guarantee, which can be a
UNIQUEconstraint rather than the primary key. In practice it is nearly always the primary key. - “A foreign key must be unique.” The opposite: it is normally repeated. A hundred orders for one customer means that customer id appears a hundred times.
- “A table can have several primary keys.” One, which may be made of several columns. Several unique constraints, yes; several primary keys, no.
- “A primary key can be NULL if it is composite.” No column of a primary key can be NULL.
- “Foreign keys slow everything down, so leave them out.” They cost a check per write and buy a guarantee that no amount of application code can provide. Index the column and the cost is small; the data you save is not.
Practice this topic
Reading explains the idea; producing it yourself is what makes it stick. These exercises use exactly what this page covers, and your answer is checked by running it against the same sample database:
- Put a name to the departmentMedium
- Every department, even the quiet onesHard
- Name the managerHard
- Customers with a shipped orderMedium
Frequently asked questions
What is the difference between a primary key and a foreign key?
A primary key uniquely identifies each row in its own table and can never be NULL. A foreign key is a column that references a row in another table, so its values may repeat and may be NULL unless declared NOT NULL. A table has exactly one primary key and as many foreign keys as it needs.
Can a foreign key be NULL?
Yes, unless the column is also declared NOT NULL. A NULL foreign key means there is no related row yet, which is often correct - an order not yet assigned to an employee, for example. Any non-NULL value must exist in the referenced table.
Can a table have more than one primary key?
No. It has at most one, although that one key may be composed of several columns, which is called a composite key. If you need to enforce uniqueness on other columns as well, add UNIQUE constraints - there is no limit on those.
Should I use an email address as a primary key?
Usually not. A primary key should never change, and email addresses do; when one changes, every foreign key referencing it has to change with it. Use a surrogate integer or UUID as the key and put a UNIQUE constraint on the email, which gives the same guarantee without the coupling.
What is a composite primary key?
A primary key made of two or more columns, where the combination must be unique even though each column alone is not. It is the natural design for a table recording a relationship, such as enrolments keyed by student and course together.
What does ON DELETE CASCADE do?
It deletes the child rows automatically when the parent row is deleted, and it chains through further child tables. It is right when a child cannot exist without its parent, such as order lines. The default, RESTRICT, refuses the delete instead, which is the safer choice whenever the children have independent value.
Related reading
About the author
SQL Practice
SQL Practice publishes free SQL tutorials, an in-browser playground and 42 practice exercises, built so you can run what you read. Every runnable example in our tutorials is executed against the site’s sample database before it is published. Read how we keep tutorials accurate, or report a mistake.