Understanding Primary Keys and Foreign Keys
Learn how primary keys uniquely identify rows, how foreign keys connect related tables, and how referential actions protect relational data.
In this lesson, you will learn
- ✓Define primary keys and explain their uniqueness and non-null requirements
- ✓Distinguish surrogate keys from natural keys
- ✓Define single-column and composite primary keys
- ✓Establish relationships using foreign key constraints
- ✓Explain ON DELETE and ON UPDATE referential actions
- ✓Choose suitable referential actions for common database scenarios
- ✓Identify when indexes on foreign key columns are useful
- ✓Diagnose common primary key and foreign key constraint errors
Why Do Databases Need Keys?
Imagine a customers table containing thousands of people. Several customers may share the same name, and two people might even share the same name and city. A name alone is therefore not a reliable way to identify a specific customer. A key gives a row a dependable identity or connects it to a row in another table. Relational databases use primary keys and foreign keys to support these two jobs.
Why Names Are Not Reliable Identifiers
The two customers have the same name but different IDs. The primary key identifies each row without relying on the name being unique.
Understanding Primary Keys
A primary key (PK) is a constraint that identifies each row in a table. Its value must be unique and cannot be NULL. A table can have only one primary key constraint, although that constraint can include one column or several columns. In PostgreSQL, defining a primary key also creates a unique B-tree index to enforce uniqueness and support lookups.
Defining a Primary Key
The primary key prevents two rows from sharing the same student_id and prevents a row from having a missing student_id.
Remember These Three Rules
Surrogate Keys and Natural Keys
A surrogate key is an identifier created for database use, such as an identity integer or UUID. A natural key is an existing business attribute that can identify a record, such as a carefully defined product code. Surrogate keys are often convenient because they are independent of changing business details. Natural keys can also be appropriate when the business guarantees that an attribute is unique and stable.
Do Not Assume Every Natural Key Is Stable
| Aspect | Surrogate Key | Natural Key |
|---|---|---|
| Source | Created for identification | Comes from a real-world or business attribute |
| Example | customer_id | A guaranteed-unique customer number |
| Change risk | Usually low | Depends on whether the business attribute can change |
| Common use | Internal row identification | Business identifiers and external references |
Creating an Auto-Generated Primary Key
In PostgreSQL, GENERATED ALWAYS AS IDENTITY automatically generates values for a column. Adding PRIMARY KEY separately makes that column the table's primary key. Identity generation and primary key enforcement solve different problems: identity generates values, while the primary key enforces uniqueness and non-nullness.
An Identity Primary Key
You omit product_id from the INSERT because PostgreSQL generates it. The primary key ensures that generated IDs cannot identify two different rows.
Understanding Composite Primary Keys
A composite primary key contains two or more columns. The combination must be unique, even though individual columns may repeat. Composite keys are useful for records whose identity naturally depends on a combination of values, such as the membership of a student in a course.
A Composite Primary Key
A student can enroll in multiple courses, and a course can have multiple students. The pair student_id and course_id uniquely identifies each enrollment.
What Is a Foreign Key?
A foreign key (FK) is a constraint on one or more columns in a child table. It requires each non-null foreign key value to match a referenced key in the parent table. The referenced columns must be a primary key or an eligible unique key. This prevents a child row from referring to a parent that does not exist.
A Foreign Key Relationship
The foreign key enforces the relationship. For applications and tutorials, use the actual generated customer ID rather than assuming it will always be 1.
Parent and Child
Seeing a Foreign Key Reject Invalid Data
A foreign key protects the relationship even when data is inserted directly through a SQL client rather than through an application. If the referenced customer does not exist, PostgreSQL rejects the order.
An Invalid Reference
The referenced customer row is missing, so the database rejects the order. The exact error wording may vary by PostgreSQL version and constraint name.
Foreign Keys and NULL Values
A foreign key column can contain NULL unless it is also declared NOT NULL. NULL means there is no referenced value for that row; it does not mean that a parent with an ID of NULL must exist. If the relationship is mandatory, use NOT NULL with the foreign key. If the relationship is optional, NULL may be appropriate.
Mandatory and Optional Relationships
NOT NULL controls whether the relationship is required. The foreign key controls whether a supplied non-null value refers to a valid department.
Choosing ON DELETE Actions
A foreign key's ON DELETE action defines what happens to referencing child rows when a parent row is deleted. Choose the action according to the meaning of the data. PostgreSQL uses NO ACTION when no delete action is specified. RESTRICT and NO ACTION both prevent a deletion that would violate the relationship in ordinary immediate-constraint cases, but they differ in when the check is performed when deferred constraints are involved.
| Action | Behavior | Suitable example |
|---|---|---|
| NO ACTION | Rejects a deletion if the resulting data would violate the foreign key; this is the default. | Preserving referenced records |
| RESTRICT | Rejects a parent deletion while referencing child rows exist. | Prevent deleting a customer with orders |
| CASCADE | Deletes referencing child rows automatically. | Removing an order and its dependent draft line items |
| SET NULL | Sets the referencing foreign key columns to NULL. | Removing an optional assignee while keeping a ticket |
| SET DEFAULT | Sets the referencing foreign key columns to their declared defaults; those values must still satisfy the foreign key. | Assigning a fallback record when it is guaranteed to exist |
Using ON DELETE RESTRICT
RESTRICT is useful when a parent must not be deleted while dependent records exist. It forces the application or database operator to deal with the dependent data explicitly before removing the parent.
Prevent Deleting a Customer with Orders
The customer must be retained, or the dependent order must be handled according to the application's rules before deletion.
Using ON DELETE CASCADE
CASCADE automatically deletes child rows when their referenced parent row is deleted. It is appropriate when child records have no independent meaning without their parent. It can also delete many rows indirectly, so review the relationship before using it.
Deleting an Order and Its Line Items
The line items belong to the order. Deleting the order automatically removes its dependent items. Do not apply this pattern to records that must be retained for auditing or legal reasons.
Using ON DELETE SET NULL
SET NULL preserves the child row but clears its foreign key value when the parent is deleted. The affected foreign key column must allow NULL. This is useful when the relationship is optional and the child record remains meaningful without the parent.
Keep a Ticket When Its Assignee Is Removed
The ticket remains, but its assigned_agent_id becomes NULL. If the column were NOT NULL, this action would fail.
Understanding ON UPDATE Actions
ON UPDATE defines what happens to referencing rows when the referenced key value changes. Primary key values are often designed to be stable, so updates may be uncommon. With ON UPDATE CASCADE, the new referenced value propagates to the child foreign key columns. With RESTRICT or NO ACTION, an update that would break the relationship is rejected.
Propagating an Updated Key
The referenced department ID changes from 10 to 20, and the employee's foreign key is updated automatically.
Composite Foreign Keys
A foreign key can contain multiple columns. The referencing columns must match the referenced key columns in number and compatible types. Composite foreign keys are useful when a relationship depends on more than one identifier, such as an enrollment that must refer to a valid student-course pair.
Referencing a Composite Primary Key
A multi-column foreign key checks the combination as a whole. It cannot be replaced by checking each column independently when the relationship depends on their pairing.
Adding a Foreign Key to an Existing Table
You can add a foreign key after creating a table by using ALTER TABLE. PostgreSQL validates existing rows when the constraint is added unless you use a workflow such as NOT VALID followed by VALIDATE CONSTRAINT. Existing invalid references must be corrected before a normally validated constraint can be added.
Add a Relationship After Table Creation
Adding a constraint does not automatically repair bad data. Check existing rows before adding a foreign key to a populated table.
Inspecting Key Constraints
In PostgreSQL, psql provides the \d command to inspect a table's columns, indexes, and constraints. You can also query information_schema to inspect primary and foreign key relationships using portable metadata views.
Inspect Foreign Key Constraints
The metadata query helps you inspect relationships without changing the database. For schemas other than public, adjust the schema filter.
Why Foreign Key Indexes Matter
A primary key or unique constraint creates an index on the referenced key in PostgreSQL. However, PostgreSQL does not automatically create an index on the child table's referencing columns. Such an index can improve joins and help the database find dependent rows when a parent is deleted or its referenced key is updated. Whether to add one depends on table size, query patterns, and write overhead.
Index a Frequently Used Foreign Key
An index may help PostgreSQL locate matching orders efficiently. It does not guarantee that the query planner will always use the index.
Common Constraint Errors
Key constraints protect data by rejecting invalid operations. Understanding the type of violation helps you fix the underlying problem instead of removing a useful constraint.
| Problem | Likely cause | What to check |
|---|---|---|
| Duplicate key value | A primary key or unique key value already exists. | Check whether the row already exists and whether the identifier is correct. |
| Foreign key violation on INSERT | The supplied parent ID does not exist. | Create or select the valid parent before inserting the child. |
| Foreign key violation on DELETE | A child row still references the parent. | Review dependent rows and the intended deletion policy. |
| Cannot set value to NULL | A NOT NULL constraint conflicts with SET NULL or the supplied value. | Confirm whether the relationship is optional and choose a compatible schema. |
| Cannot add foreign key | Existing rows may contain invalid references or the referenced columns are not eligible as a key. | Validate existing data and confirm the referenced key definition. |
MCQ 1: Primary Key Rules
Which statement about a primary key is correct?
MCQ 2: Composite Keys
Check whether you understand how a composite primary key works.
MCQ 3: Foreign Key Purpose
Choose the statement that best describes a foreign key.
MCQ 4: ON DELETE CASCADE
Consider the effect of cascading deletion.
MCQ 5: ON DELETE SET NULL
Consider a child table that keeps records after a referenced parent is removed.
MCQ 6: RESTRICT and NO ACTION
Understand the default behavior when a referenced parent is deleted.
MCQ 7: Foreign Key Indexes
Check your understanding of indexes on referencing columns.
MCQ 8: Mandatory Relationships
A foreign key and a NOT NULL constraint serve related but different purposes.
Guided Practice: Build a Library Relationship
Create a small library database. Each book copy belongs to a valid book record. First write the SQL yourself, then compare it with the solution. The exercise uses a surrogate primary key for each book and a foreign key from book_copies to books.
Practice Requirements
Think about which table owns the original book record and which table contains dependent copies. The copies table should reference the books table.
Guided Practice Solution
Compare your solution with this version. The foreign key prevents a copy from referencing a missing book, and RESTRICT prevents deleting a book while copies reference it.
Library Tables Solution
The INSERT ... SELECT pattern avoids assuming which identity value PostgreSQL will generate. In a real application, use a stable identifier to select the intended book if titles are not unique.
Interview Questions and Answers
Use these questions to test whether you can explain key constraints and relationship behavior in an entry-level SQL interview.
Lesson Summary
Primary keys identify rows; foreign keys connect rows across tables. A primary key must be unique and non-null, while a foreign key ensures that supplied non-null references exist. Composite keys use a combination of columns. Referential actions determine what happens when referenced keys are deleted or updated. Choose CASCADE, RESTRICT, SET NULL, and other actions according to the meaning and lifecycle of the data. In PostgreSQL, consider indexing child foreign key columns when the workload benefits from it.
Common pitfalls to avoid
Real debugging lessons and exact syntax contrasts to avoid runtime bugs
#1 Assuming a foreign key automatically makes the child column unique
Why it happens: A foreign key checks whether a referenced value exists, but it does not prohibit multiple child rows from referencing the same parent.
Key takeaway: Multiple orders can reference the same customer. Add UNIQUE to customer_id only if the business rule truly requires each customer to appear in at most one order.
#2 Using ON DELETE CASCADE without considering dependent data
Why it happens: CASCADE is convenient, but deleting one parent can automatically remove many child rows.
Key takeaway: Use RESTRICT when dependent records must be reviewed or retained. The correct action depends on the application; CASCADE is not universally wrong.
#3 Using SET NULL on a NOT NULL column
Why it happens: The referential action attempts to clear the foreign key, but the column definition forbids NULL.
Key takeaway: If the relationship is optional, allow NULL. If it is mandatory, choose another deletion policy.
#4 Assuming identity generation automatically creates a primary key
Why it happens: Automatically generated values may look unique, but value generation and uniqueness constraints are separate concerns.
Key takeaway: Declare PRIMARY KEY when the column is meant to identify each row. Identity generation alone is not a substitute for a primary key constraint.
#5 Forgetting that foreign key columns can contain duplicates
Why it happens: Learners sometimes assume that every key column must be unique.
Key takeaway: For example, many orders can belong to one customer. A unique constraint on orders.customer_id would incorrectly restrict that relationship.
#6 Assuming PostgreSQL automatically indexes the child foreign key
Why it happens: The referenced primary key is indexed, which can lead learners to assume the child side is indexed too.
Key takeaway: The index is optional and workload-dependent, but it is often useful for joins and locating child rows. Do not create indexes blindly without considering query patterns and write costs.
#7 Assuming every parent row must have a child row
Why it happens: A foreign key is mistaken for a rule that enforces relationships in both directions.
Key takeaway: A foreign key protects references from child to parent; it does not require each parent to be referenced.
Ready for hands-on challenges?
Test your skills with shuffled coding challenges in the Practice Arena.
Understanding Primary Keys and Foreign Keys Knowledge Check
Test your understanding with immediate feedback
Which statement about a primary key is correct?
A table has PRIMARY KEY (student_id, course_id). Which pair is allowed?
What does a foreign key constraint enforce?
What happens when a parent row is deleted under ON DELETE CASCADE?
What is required for ON DELETE SET NULL to work for a child foreign key column?
If a foreign key has no ON DELETE clause, what is PostgreSQL's default action?
In PostgreSQL, does defining a foreign key automatically create an index on the child foreign key column?
How do you require every order to reference an existing customer?
Technical and placement interview questions
Frequently tested interview questions with detailed, full answers (click to expand)
Q1:What is a primary key?
A primary key is a constraint that uniquely identifies each row. It enforces uniqueness and disallows NULL values.
Q2:Can a table have two primary keys?
No. A table can have one primary key constraint. That constraint can contain multiple columns, making it a composite primary key.
Q3:What is a foreign key?
A foreign key ensures that each non-null referencing value matches a key in another table or an eligible key in the same table.
Q4:What is the difference between a primary key and a foreign key?
A primary key identifies rows in its own table. A foreign key links a row to a referenced row and enforces referential integrity.
Q5:What is a composite primary key?
It is one primary key constraint made from two or more columns. The combination must be unique, and every component must be non-null.
Q6:What is a surrogate key?
A surrogate key is an identifier created for database use, such as an identity integer or UUID, rather than being taken directly from a business attribute.
Q7:What is a natural key?
A natural key is a real-world or business attribute that identifies a record under defined uniqueness rules.
Q8:What does ON DELETE CASCADE do?
It automatically deletes child rows that reference a parent row when that parent is deleted.
Q9:What is the difference between RESTRICT and CASCADE?
RESTRICT blocks a parent deletion while referencing child rows exist. CASCADE deletes those referencing child rows.
Q10:What does ON DELETE SET NULL do?
It preserves child rows but sets their foreign key values to NULL. The affected columns must allow NULL.
Q11:What is the default ON DELETE action in PostgreSQL?
NO ACTION. A deletion that would violate the foreign key is rejected.
Q12:Does a foreign key automatically create an index on the child column in PostgreSQL?
No. Add an index when it benefits joins, parent deletion or updates, or other common queries.
Q13:Can a foreign key contain NULL?
Yes, unless the referencing column has a NOT NULL constraint. A non-null value must match a referenced key.
Q14:Can a foreign key reference a column other than the primary key?
Yes, if the referenced column or column combination has an eligible unique key, such as a UNIQUE constraint.
Q15:Why might adding a foreign key to an existing table fail?
Existing child rows may reference missing parents, or the referenced columns may not have an eligible key constraint.
Q16:What does ON UPDATE CASCADE do?
It propagates changes to the referenced key value to referencing foreign key values.
Q17:Does a foreign key guarantee that every parent has a child?
No. It ensures that child references are valid. It does not require every parent to be referenced by a child.
Key takeaways
- A primary key uniquely identifies each row and does not allow NULL values.
- A table can have only one primary key constraint, but that key may contain multiple columns.
- A foreign key ensures that non-null referenced values match an eligible key in the referenced table.
- A foreign key may reference a primary key or another eligible unique key.
- ON DELETE CASCADE deletes dependent child rows; use it only when that behavior is intended.
- ON DELETE RESTRICT prevents deletion of a referenced parent row while dependent rows exist.
- ON DELETE SET NULL requires the child foreign key column to allow NULL values.
- Foreign key constraints do not automatically create an index on the referencing columns in PostgreSQL.
- Choose keys and referential actions according to the relationship and data lifecycle.