Lesson 8
Primary keys and foreign keys
For readers who finished part 2 and can query and summarize one table.
- The primary key names one row
- A key of two columns
- The foreign key points at another table
- What PostgreSQL refuses
- Keys in a diagram
- 4 exercises
Every query runs on real PostgreSQL, in your browser. It downloads once, about 6 MB, then starts in a few seconds.
For a reference page on the same statements, read What Is a Primary Key in SQL? Definition and Examples.
The idea
A primary key names exactly one row
This is how departments was created:
create table departments (
department_id integer primary key,
name text not null unique
);
The primary key is department_id. No two departments may have the same id, and none may be without one, so a department id always means one department and only one.
Try it
Add a department whose id is taken
insert into departments values (1, 'Purchasing');
PostgreSQL refuses the row and names the key in its message: departments_pkey is the primary key of departments.
The idea
A key can take two columns

An order line is named by its order and its number within that order: the table ends with primary key (order_id, line_no). Many lines have line_no 1, one per order, and an order has several lines. Only the pair is unique: (7, 1) is line 1 of order 7.
The idea
A foreign key points at a row of another table
In orders, customer_id integer not null references customers makes customer_id a foreign key: its value must be the id of a customer that exists.
The line in the diagram is that key. It runs from the column that points to the table it points at.
Try it
Add an order for a customer who does not exist
insert into orders values (5000, 999, null, date '2026-10-17', 'pending');
The foreign key orders_customer_id_fkey refuses an order that would point at nobody.
Try it
Delete a customer who has orders
delete from customers where customer_id = 1;
The same key works the other way: a customer that orders point at cannot go, or those orders would point at nobody. Lesson 13 deletes rows in the order the keys allow.
The idea
The lines of a diagram are the foreign keys
Every line is a foreign key: an order points at a customer and an employee, an order line at an order and a product, an employee at a department and at their manager. Lesson 9 joins tables along these lines.
In DbSchema
Open a foreign key to see what it links

In a DbSchema diagram, a double-click on a line opens the Edit Foreign Key dialog: the table it points at, and which column pairs with which. Connect DbSchema to your own PostgreSQL and it draws these lines from the keys your tables already have.
The Community Edition is free. Every download starts with a 15-day trial of all features.
Exercise 1 of 4
Which columns form the primary key of order_items?
Each answer sends the exercise and whether the answer was right to dbschema.com, without your IP address.
Exercise 2 of 4
Add a department named Purchasing, with id 8.
Expected result
It shows once PostgreSQL is ready.
Each check sends the exercise, your SQL and any PostgreSQL error to dbschema.com, without your IP address, so that the hints can improve.
Show answer
insert into departments values (8, 'Purchasing')
Exercise 3 of 4
What happens when you add an order for customer 999, who does not exist? Run it here first if you like.
insert into orders values (5000, 999, null, date '2026-10-17', 'pending');
Each answer sends the exercise and whether the answer was right to dbschema.com, without your IP address.
Exercise 4 of 4
Create a table reviews with four integer columns: review_id as its primary key, product_id that must be a product that exists, customer_id that must be a customer that exists, and rating.
Expected result
It shows once PostgreSQL is ready.
Each check sends the exercise, your SQL and any PostgreSQL error to dbschema.com, without your IP address, so that the hints can improve.
Show answer
create table reviews (review_id integer primary key, product_id integer not null references products, customer_id integer not null references customers, rating integer not null check (rating between 1 and 5))