Learn with Yasir

Share Your Feedback

PostgreSQL JOIN Tables


Learn how to use the PostgreSQL WHERE clause in SELECT statements to filter records with comparison operators, logical conditions, and practical SQL examples.

1. What is a JOIN?

In a database, information is usually divided into different tables.

For example, in the DVD Rental database:

  • customer stores customer information.
  • payment stores payment information.
  • rental stores rental information.
  • film stores film information.
  • inventory stores available copies of films.
  • staff stores staff information.

These tables are related through keys.

For example:

customer
---------
customer_id  ←──────────────┐
first_name                  │
last_name                   │
                            │
payment                     │
---------                   │
payment_id                  │
customer_id ────────────────┘
amount
payment_date

A JOIN allows us to combine related information from two or more tables.

Simple definition

JOIN is used to retrieve related data from multiple tables.


2. Why Do We Use JOIN?

Suppose we run:

SELECT *
FROM customer;

We get customer information.

If we run:

SELECT *
FROM payment;

We get payment information.

But suppose we want to know:

Which customer made each payment?

The payment table contains customer_id, but we also need the customer’s name from the customer table.

We can JOIN the tables:

SELECT
    customer.first_name,
    customer.last_name,
    payment.amount
FROM customer
JOIN payment
    ON customer.customer_id = payment.customer_id;

Result will look conceptually like:

first_name last_name amount
Mary Smith 7.99
Patricia Johnson 4.99
Linda Williams 2.99

The JOIN connects:

customer.customer_id
        =
payment.customer_id

3. Understanding Keys Before JOIN

Students should understand these two terms:

Primary Key

A Primary Key (PK) uniquely identifies a record.

Example:

customer
----------------
customer_id  PK
first_name
last_name

customer_id uniquely identifies each customer.

Foreign Key

A Foreign Key (FK) refers to a primary key in another table.

Example:

payment
----------------
payment_id
customer_id  FK
amount

Here:

payment.customer_id
        ↓
customer.customer_id

This relationship allows us to JOIN the tables.


4. Basic JOIN Syntax

SELECT columns
FROM table1
JOIN table2
    ON table1.column = table2.column;

Example:

SELECT
    customer.first_name,
    customer.last_name,
    payment.amount
FROM customer
JOIN payment
    ON customer.customer_id = payment.customer_id;

5. Types of JOINs

The important JOIN types in PostgreSQL are:

  1. INNER JOIN
  2. LEFT JOIN
  3. RIGHT JOIN
  4. FULL OUTER JOIN
  5. CROSS JOIN
  6. SELF JOIN
  7. NATURAL JOIN

We will use the DVD Rental database for practical examples.


6. Table Aliases

Before learning JOINs, students should understand table aliases.

An alias gives a table a short name.

Instead of:

customer.customer_id

we can write:

c.customer_id

Example:

SELECT
    c.first_name,
    c.last_name,
    p.amount
FROM customer AS c
JOIN payment AS p
    ON c.customer_id = p.customer_id;

AS is optional:

FROM customer c
JOIN payment p

Both are valid.

Why use aliases?

Aliases make JOIN queries:

  • shorter
  • easier to read
  • easier to write
  • especially useful when multiple tables have columns with the same name

7. INNER JOIN

What is INNER JOIN?

An INNER JOIN returns only records that have a matching record in both tables.

Think:

Table A       Table B
   ○────────────○
      Matching
       records

Example

Find customers and their payments:

SELECT
    c.customer_id,
    c.first_name,
    c.last_name,
    p.amount
FROM customer AS c
INNER JOIN payment AS p
    ON c.customer_id = p.customer_id;

What happens?

PostgreSQL finds:

customer.customer_id
        =
payment.customer_id

and combines matching rows.

Important

JOIN

and

INNER JOIN

normally mean the same thing.

So this:

FROM customer
JOIN payment
    ON customer.customer_id = payment.customer_id

is equivalent to:

FROM customer
INNER JOIN payment
    ON customer.customer_id = payment.customer_id

8. Practical INNER JOIN Example

Question

Show the first name, last name, and payment amount of customers.

SELECT
    c.first_name,
    c.last_name,
    p.amount
FROM customer c
INNER JOIN payment p
    ON c.customer_id = p.customer_id;

Add a WHERE condition

Find payments greater than 5:

SELECT
    c.first_name,
    c.last_name,
    p.amount
FROM customer c
INNER JOIN payment p
    ON c.customer_id = p.customer_id
WHERE p.amount > 5;

This demonstrates that JOIN and WHERE can be used together.


9. Joining Three Tables

JOIN is not limited to two tables.

Suppose we want:

Customer name + rental date + film title

We need:

customer
   ↓
rental
   ↓
inventory
   ↓
film

Query:

SELECT
    c.first_name,
    c.last_name,
    r.rental_date,
    f.title
FROM customer c
JOIN rental r
    ON c.customer_id = r.customer_id
JOIN inventory i
    ON r.inventory_id = i.inventory_id
JOIN film f
    ON i.film_id = f.film_id;

This is a very useful real-world example.


10. LEFT JOIN

What is LEFT JOIN?

A LEFT JOIN returns:

All records from the left table + matching records from the right table.

If there is no match, PostgreSQL returns NULL for the right table’s columns.

Think:

LEFT TABLE          RIGHT TABLE

████████████████
████████████████
████████████████
       + 
    matching

Example

SELECT
    c.customer_id,
    c.first_name,
    p.payment_id,
    p.amount
FROM customer c
LEFT JOIN payment p
    ON c.customer_id = p.customer_id;

Meaning

Show all customers, including customers who do not have a matching payment.

If a customer has no payment:

customer_id first_name payment_id amount
101 John NULL NULL

The customer is still shown because customer is the left table.


11. Why LEFT JOIN Is Useful

Suppose the question is:

Show all customers, including customers who have never made a payment.

Use:

SELECT
    c.customer_id,
    c.first_name,
    c.last_name,
    p.payment_id
FROM customer c
LEFT JOIN payment p
    ON c.customer_id = p.customer_id;

The LEFT JOIN keeps every customer.


12. Find Records with No Match

A very useful technique:

SELECT
    c.customer_id,
    c.first_name,
    c.last_name
FROM customer c
LEFT JOIN payment p
    ON c.customer_id = p.customer_id
WHERE p.payment_id IS NULL;

Meaning:

Find customers who have no payment record.

This pattern is important:

LEFT JOIN ...
WHERE right_table.id IS NULL

It is commonly used to find records without a matching record.


13. RIGHT JOIN

A RIGHT JOIN returns:

All records from the right table + matching records from the left table.

Example:

SELECT
    c.first_name,
    c.last_name,
    p.payment_id,
    p.amount
FROM customer c
RIGHT JOIN payment p
    ON c.customer_id = p.customer_id;

The important table here is the right table, payment.


LEFT JOIN vs RIGHT JOIN

LEFT JOIN

FROM customer c
LEFT JOIN payment p

Keeps all customers.

RIGHT JOIN

FROM customer c
RIGHT JOIN payment p

Keeps all payments.

Simple rule

LEFT JOIN → keep everything from the left table.

RIGHT JOIN → keep everything from the right table.

In practice, many developers prefer LEFT JOIN because it is often easier to read. A RIGHT JOIN can usually be rewritten as a LEFT JOIN by reversing the table order.


14. FULL OUTER JOIN

A FULL OUTER JOIN returns:

  • matching rows
  • unmatched rows from the left table
  • unmatched rows from the right table

Conceptually:

LEFT TABLE      RIGHT TABLE
████████████████████████████
████████████████████████████
████████████████████████████
      ALL RECORDS

Syntax:

SELECT ...
FROM table1
FULL OUTER JOIN table2
    ON table1.column = table2.column;

DVD Rental Example

For demonstration:

SELECT
    c.customer_id,
    c.first_name,
    p.payment_id,
    p.amount
FROM customer c
FULL OUTER JOIN payment p
    ON c.customer_id = p.customer_id;

This returns all rows from both tables.

Important DVD Rental observation

In the standard DVD Rental database, customers and payments are related through customer_id, so most/all payment records normally have a matching customer. Therefore, this example is useful for learning the syntax, but it may not visibly demonstrate unmatched rows.


15. SELF JOIN

A SELF JOIN means joining a table with itself.

Why would we do this?

Sometimes records in the same table are related to each other.

A classic example is an employee table:

employee
----------------
employee_id
employee_name
manager_id

Here:

employee.manager_id
        ↓
employee.employee_id

The same table can represent both:

  • employee
  • manager

DVD Rental SELF JOIN Example

The DVD Rental staff table contains staff members. For a simple demonstration of SELF JOIN, we can compare staff records.

SELECT
    s1.first_name AS staff1,
    s2.first_name AS staff2
FROM staff s1
JOIN staff s2
    ON s1.store_id = s2.store_id
WHERE s1.staff_id <> s2.staff_id;

What is happening?

We use the same table twice:

staff s1
   +
staff s2

The aliases s1 and s2 allow PostgreSQL to treat the same table as two different references.


16. Another SELF JOIN Example

Find pairs of staff members working at the same store:

SELECT
    s1.first_name AS staff_member_1,
    s2.first_name AS staff_member_2,
    s1.store_id
FROM staff s1
JOIN staff s2
    ON s1.store_id = s2.store_id
WHERE s1.staff_id < s2.staff_id;

Why use:

s1.staff_id < s2.staff_id

?

To avoid getting duplicate pairs such as:

Mike - Jon
Jon - Mike

We only want one pair.


17. CROSS JOIN

A CROSS JOIN produces every possible combination of rows from two tables.

Suppose:

Table A = 3 rows
Table B = 4 rows

CROSS JOIN produces:

3 × 4 = 12 rows

Syntax

SELECT *
FROM table1
CROSS JOIN table2;

DVD Rental Example

SELECT
    c.first_name,
    c.last_name,
    s.first_name AS staff_first_name
FROM customer c
CROSS JOIN staff s;

This produces every customer–staff combination.

If there are 599 customers and 2 staff members:

599 × 2 = 1198 rows

Important

A CROSS JOIN does not require an ON condition.


18. When Would We Use CROSS JOIN?

CROSS JOIN is useful when we intentionally need all possible combinations.

For example:

Products × Sizes

could produce:

Shirt + Small
Shirt + Medium
Shirt + Large
Shoes + Small
Shoes + Medium
Shoes + Large

But be careful:

CROSS JOIN can produce a very large number of rows.


19. NATURAL JOIN

A NATURAL JOIN automatically joins tables using columns that have the same name.

Syntax:

SELECT *
FROM table1
NATURAL JOIN table2;

PostgreSQL automatically looks for columns with matching names.


DVD Rental Example

customer and payment both have:

customer_id

So we could write:

SELECT
    first_name,
    last_name,
    amount
FROM customer
NATURAL JOIN payment;

PostgreSQL automatically uses the common column:

customer_id

20. Why Be Careful with NATURAL JOIN?

NATURAL JOIN can be convenient, but it is generally less explicit.

Suppose today two tables have:

customer_id

as the only common column.

Later, a new column with the same name is added to both tables.

The behavior of the NATURAL JOIN could change automatically.

Therefore, beginners should generally prefer:

JOIN ...
ON ...

because it clearly tells us which columns are being used for the relationship.

For example:

SELECT
    c.first_name,
    c.last_name,
    p.amount
FROM customer c
JOIN payment p
    ON c.customer_id = p.customer_id;

This is much easier to understand.


21. JOIN with WHERE

JOIN and WHERE have different jobs.

JOIN

Connects related tables.

ON c.customer_id = p.customer_id

WHERE

Filters the result.

WHERE p.amount > 5

Example:

SELECT
    c.first_name,
    c.last_name,
    p.amount
FROM customer c
JOIN payment p
    ON c.customer_id = p.customer_id
WHERE p.amount > 5;

Think:

JOIN → Connect tables
WHERE → Filter rows

22. JOIN with ORDER BY

We can also sort the result.

SELECT
    c.first_name,
    c.last_name,
    p.amount
FROM customer c
JOIN payment p
    ON c.customer_id = p.customer_id
ORDER BY p.amount DESC;

This shows the highest payment amounts first.


23. JOIN with GROUP BY

We can calculate the total payment made by each customer.

SELECT
    c.customer_id,
    c.first_name,
    c.last_name,
    SUM(p.amount) AS total_payment
FROM customer c
JOIN payment p
    ON c.customer_id = p.customer_id
GROUP BY
    c.customer_id,
    c.first_name,
    c.last_name
ORDER BY total_payment DESC;

This is a very practical example of JOIN + aggregate functions.


24. JOIN + GROUP BY + HAVING

Find customers whose total payments are greater than 100:

SELECT
    c.customer_id,
    c.first_name,
    c.last_name,
    SUM(p.amount) AS total_payment
FROM customer c
JOIN payment p
    ON c.customer_id = p.customer_id
GROUP BY
    c.customer_id,
    c.first_name,
    c.last_name
HAVING SUM(p.amount) > 100;

Remember:

JOIN     → connect tables
WHERE    → filter individual rows
GROUP BY → create groups
HAVING   → filter groups

25. JOIN with Multiple Tables — Practical Example

Question

Show the customer name, film title, and rental date.

We need four tables:

customer
   ↓
rental
   ↓
inventory
   ↓
film

Query:

SELECT
    c.first_name,
    c.last_name,
    f.title,
    r.rental_date
FROM customer c
JOIN rental r
    ON c.customer_id = r.customer_id
JOIN inventory i
    ON r.inventory_id = i.inventory_id
JOIN film f
    ON i.film_id = f.film_id;

This is one of the best examples for understanding why JOINs are important.


26. Practical Example — Customer + Film + Payment

Suppose we want:

Customer name, film title, payment amount.

We can use:

SELECT
    c.first_name,
    c.last_name,
    f.title,
    p.amount
FROM customer c
JOIN payment p
    ON c.customer_id = p.customer_id
JOIN rental r
    ON p.rental_id = r.rental_id
JOIN inventory i
    ON r.inventory_id = i.inventory_id
JOIN film f
    ON i.film_id = f.film_id;

Conceptually:

customer
    │
    │ customer_id
    ↓
payment
    │
    │ rental_id
    ↓
rental
    │
    │ inventory_id
    ↓
inventory
    │
    │ film_id
    ↓
film

27. JOIN Types — Quick Comparison

JOIN What does it return?
INNER JOIN Matching records from both tables
LEFT JOIN All left records + matching right records
RIGHT JOIN All right records + matching left records
FULL OUTER JOIN All records from both tables
CROSS JOIN Every possible combination
SELF JOIN A table joined with itself
NATURAL JOIN Automatically joins columns with the same names

28. Visual Understanding

INNER JOIN

Table A       Table B

   AAAAA
      BBBBB
       ↑
     MATCH

Only matching records.

LEFT JOIN

Table A       Table B

AAAAAAAAA
    BBB

All A + matching B.

RIGHT JOIN

Table A       Table B

    AAA
BBBBBBBBB

Matching A + all B.

FULL OUTER JOIN

Table A       Table B

AAAAAAAAA BBBBBBBBB
       ALL

Everything from both tables.

CROSS JOIN

A × B

A1 → B1
A1 → B2
A2 → B1
A2 → B2
...

Every combination.


29. The Most Important JOIN Pattern

Students should first master this pattern:

SELECT
    table1.column,
    table2.column
FROM table1
JOIN table2
    ON table1.common_column = table2.common_column;

For DVD Rental:

SELECT
    c.first_name,
    c.last_name,
    p.amount
FROM customer c
JOIN payment p
    ON c.customer_id = p.customer_id;

Then add filtering:

WHERE p.amount > 5;

Then sorting:

ORDER BY p.amount DESC;

Complete query:

SELECT
    c.first_name,
    c.last_name,
    p.amount
FROM customer c
JOIN payment p
    ON c.customer_id = p.customer_id
WHERE p.amount > 5
ORDER BY p.amount DESC;

30. Beginner Rules to Remember

Rule 1 — JOIN connects tables

JOIN payment p
ON c.customer_id = p.customer_id

Rule 2 — ON specifies the relationship

ON table1.id = table2.id

Rule 3 — WHERE filters records

WHERE p.amount > 5

Rule 4 — LEFT JOIN keeps everything from the left table

FROM customer c
LEFT JOIN payment p

Rule 5 — RIGHT JOIN keeps everything from the right table

FROM customer c
RIGHT JOIN payment p

Rule 6 — INNER JOIN keeps only matches

INNER JOIN

Rule 7 — CROSS JOIN creates combinations

CROSS JOIN

Rule 8 — SELF JOIN joins a table with itself

staff s1
JOIN staff s2

Rule 9 — NATURAL JOIN automatically finds same-named columns

NATURAL JOIN

Rule 10 — Prefer explicit JOIN conditions

For beginner and professional SQL, this is usually clearer:

JOIN payment p
ON c.customer_id = p.customer_id

rather than relying on:

NATURAL JOIN

31. Practice Exercises — DVD Rental

Try these without looking at the answers first.

Exercise 1

Display customer first name, last name, and payment amount.

Exercise 2

Display customer names and payments greater than 5.

Exercise 3

Display all customers and their payments, including customers with no payment.

Exercise 4

Find customers who have no payment records.

Exercise 5

Display customer name, rental date, and film title.

Exercise 6

Display film titles and their rental rates.

Exercise 7

Display each customer’s total payment.

Exercise 8

Display customers whose total payment is greater than 100.

Exercise 9

Display all possible customer and staff combinations using CROSS JOIN.

Exercise 10

Use a SELF JOIN on the staff table to find pairs of staff members working at the same store.

Exercise 11

Use NATURAL JOIN to join customer and payment.

Exercise 12

Rewrite the NATURAL JOIN query using an explicit JOIN ... ON condition.


Final Cheat Sheet

-- INNER JOIN
SELECT *
FROM customer c
JOIN payment p
ON c.customer_id = p.customer_id;


-- LEFT JOIN
SELECT *
FROM customer c
LEFT JOIN payment p
ON c.customer_id = p.customer_id;


-- RIGHT JOIN
SELECT *
FROM customer c
RIGHT JOIN payment p
ON c.customer_id = p.customer_id;


-- FULL OUTER JOIN
SELECT *
FROM customer c
FULL OUTER JOIN payment p
ON c.customer_id = p.customer_id;


-- CROSS JOIN
SELECT *
FROM customer c
CROSS JOIN staff s;


-- SELF JOIN
SELECT *
FROM staff s1
JOIN staff s2
ON s1.store_id = s2.store_id;


-- NATURAL JOIN
SELECT *
FROM customer
NATURAL JOIN payment;

One-line memory trick

**INNER = matching LEFT = all left RIGHT = all right FULL = everything CROSS = combinations SELF = same table NATURAL = same-named columns**