ToolPopToolPop
SQL · Lesson 4 of 8

JOINs without the panic

12 min readUpdated 24 Jun 2026

JOINs are where most people freeze. They look scary. They are not. A JOIN is just glue between two tables, and there are really only two flavours you need to remember.

Once this clicks, your queries go from "list of stuff" to "actual answers to business questions." This is the unlock.

Diagram
rendering diagram...
Two tables, one join, one combined row

Why we even need JOINs

Your customers live in a customers table. Their orders live in an orders table. The orders table just has a customer_id, not the full customer name. So to print "Priya placed an order for 450 rupees," you need to glue the two tables together using that ID.

That gluing is a JOIN. The column you glue on is usually an ID.

INNER JOIN: only matches

The default. Returns rows where the join condition matches in both tables.

sql
SELECT c.name, o.total
FROM customers c
INNER JOIN orders o ON o.customer_id = c.id;
  • c and o are aliases, short nicknames for the tables. Saves typing and clarifies which column came from where.
  • ON says how the tables connect.
  • If a customer has zero orders, they do not show up. INNER JOIN is strict.

LEFT JOIN: keep everyone on the left

When you want all rows from the left table, even if there is no match on the right.

sql
SELECT c.name, o.total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;

Customers with no orders still appear, but o.total will be NULL for them. This is exactly how you answer questions like "which customers have not ordered in six months."

Rule of thumb: start with INNER JOIN. If you find rows missing that you expected to see, switch to LEFT JOIN.

RIGHT JOIN: yes it exists, no you don't need it

RIGHT JOIN is LEFT JOIN with the tables flipped. Anything you can write as RIGHT JOIN, you can write as LEFT JOIN by swapping the table order. Every team I have worked on, by convention, just uses LEFT JOIN. Less mental gymnastics.

Multi-table JOINs

You can keep going. Customers, orders, restaurants:

sql
SELECT c.name, r.name AS restaurant, o.total
FROM customers c
JOIN orders o ON o.customer_id = c.id
JOIN restaurants r ON r.id = o.restaurant_id;

Each JOIN adds one more table. Build it one JOIN at a time, run it, check the count makes sense, then add the next one. Do not write all three at once and pray.

The cartesian product mistake

Forget the ON clause and SQL will happily pair every row in table A with every row in table B. 10,000 customers and 50,000 orders becomes 500 million rows. Your query hangs. Your DB cries.

Always include ON. If your row count explodes, that is the first thing to check. I have killed a production query at 2am for exactly this reason.

Try it on the right

Three JOIN assignments. Use aliases. Read the ON clause carefully. Next: GROUP BY and the aggregate functions that turn rows into insights.

Free tools you can use while you learn

Common questions

Q.When should I use INNER JOIN vs LEFT JOIN?
A.Use INNER JOIN when you only want rows that exist in both tables. Use LEFT JOIN when you want to keep every row from the left table even when there is no match on the right (good for "who has NOT done X" queries).
Q.Why is my JOIN returning duplicate rows?
A.Almost always because the ON clause matches more than one row on the right side. Add a more specific condition, or use DISTINCT or a GROUP BY to collapse the duplicates.
Q.How many tables can I JOIN at once?
A.As many as your query needs. Performance falls off after about a dozen joins on large tables, but for 3-5 table joins on indexed columns SQL is comfortable.
Chai0/2 done

Watching quietly. Tap me if you want a tip.

SQL Playground

Try this (0 of 2 done)

  1. 1

    Show each delivered order with the customer's name and the restaurant's name.

    hint

    Join customers AND restaurants onto orders.

    show answer
    SELECT c.name AS customer, r.name AS restaurant, o.total
    FROM orders o
    JOIN customers c ON c.id = o.customer_id
    JOIN restaurants r ON r.id = o.restaurant_id
    WHERE o.status = 'delivered';
  2. 2

    Show every customer with their orders. Include customers who haven't ordered yet (LEFT JOIN).

    hint

    LEFT JOIN orders ON orders.customer_id = customers.id

    show answer
    SELECT c.name, o.id AS order_id
    FROM customers c
    LEFT JOIN orders o ON o.customer_id = c.id;