SQL JOIN Explained for Beginners: INNER, LEFT & More
Learn SQL JOIN for beginners: combine rows from two tables with INNER and LEFT JOIN, using simple copy-paste examples and clear result tables to follow.
Learn SQL JOIN for beginners: combine rows from two tables with INNER and LEFT JOIN, using simple copy-paste examples and clear result tables to follow.
If you searched for "SQL JOIN explained for beginners," here is the short answer: a JOIN is a way to combine rows from two or more tables based on a column they share, so you can pull related data together in one query. This guide walks through exactly how that works, with small examples you can copy, paste, and run yourself.
You do not need any prior SQL knowledge beyond the basics of SELECT and WHERE. If those are new to you, it may help to read our guide on SQL SELECT and WHERE first, then come back here.
In a real database, information is usually split across several tables instead of crammed into one giant table. For example, a shop might keep customer details in a customers table and order details in a separate orders table. Splitting data like this avoids repeating the same customer name on every single order row, which saves space and avoids mistakes.
The two tables are linked by an ID column. The customers table has a primary key, usually called id, which uniquely identifies each customer. The orders table has a foreign key, often called customer_id, which stores the id of the customer who placed that order. A JOIN uses this shared value to stitch the two tables back together when you need both pieces of information at once.
To keep things concrete, imagine a tiny shop database with these two tables.
| id | name | city |
|---|---|---|
| 1 | Maria | Lisbon |
| 2 | Sam | Austin |
| 3 | Noor | Cairo |
That is the customers table. Now here is the orders table, where customer_id points back to a row in customers.
| id | customer_id | product |
|---|---|---|
| 101 | 1 | Headphones |
| 102 | 1 | Phone Case |
| 103 | 2 | Laptop Stand |
| 104 | 5 | Keyboard |
Notice that Noor (customer id 3) has no orders, and order 104 has a customer_id of 5, which does not exist in the customers table. This is realistic and messy on purpose, so you can see exactly how each JOIN type handles it.
INNER JOIN is the most common join. It returns only the rows where the shared value exists in both tables, and drops everything else.
SELECT customers.name, orders.product
FROM customers
INNER JOIN orders
ON customers.id = orders.customer_id;Line by line: SELECT customers.name, orders.product picks the two columns we want, one from each table. FROM customers sets the starting table. INNER JOIN orders adds the second table we want to combine with it. ON customers.id = orders.customer_id tells SQL which columns to match up, this is the rule that connects the two tables.
The result only includes customers who have at least one order, and only orders whose customer_id matches a real customer. So Noor (no orders) and order 104 (unknown customer) are both left out.
| name | product |
|---|---|
| Maria | Headphones |
| Maria | Phone Case |
| Sam | Laptop Stand |
Sometimes you want every row from your main table even when there is no match in the other table, for example, to see every customer including ones who never ordered anything. LEFT JOIN does exactly that.
SELECT customers.name, orders.product
FROM customers
LEFT JOIN orders
ON customers.id = orders.customer_id;This looks almost identical to the INNER JOIN query, only the word LEFT is new. The FROM table (customers) is the left side, and every row from it is kept no matter what. When there is no matching order, SQL fills the orders.product column with NULL, which simply means empty or unknown.
| name | product |
|---|---|
| Maria | Headphones |
| Maria | Phone Case |
| Sam | Laptop Stand |
| Noor | NULL |
This time Noor still shows up, with NULL in place of a product, because she matched no rows in orders. The unmatched order 104 is still dropped, because RIGHT-side rows without a match are not kept in a LEFT JOIN.
RIGHT JOIN is the mirror image of LEFT JOIN: it keeps every row from the second (right) table and fills in NULL for the first table when there is no match. FULL JOIN keeps every row from both tables, matched or not, filling NULL on whichever side is missing. RIGHT JOIN and FULL JOIN are less common in practice, because most people just rewrite the table order and use LEFT JOIN instead. One thing to note: MySQL does not support FULL JOIN directly, while PostgreSQL does.
SELECT c.name, o.product
FROM customers AS c
LEFT JOIN orders AS o
ON c.id = o.customer_id
WHERE c.city = 'Lisbon';AS c and AS o give each table a short nickname, so c.name and o.product are quicker to type and read than customers.name and orders.product. The WHERE clause still works normally after a JOIN, here it filters the combined result down to customers based in Lisbon.
New to SQL and not sure what SELECT and WHERE even do? Start one step earlier with our beginner guide.
Read the SQL SELECT and WHERE guideYou can practice the exact examples from this guide in a free online SQL sandbox, or on your own computer using SQLite, PostgreSQL, or MySQL. Here is the full setup so you can run everything above yourself.
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT,
city TEXT
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER,
product TEXT
);
INSERT INTO customers VALUES
(1, 'Maria', 'Lisbon'),
(2, 'Sam', 'Austin'),
(3, 'Noor', 'Cairo');
INSERT INTO orders VALUES
(101, 1, 'Headphones'),
(102, 1, 'Phone Case'),
(103, 2, 'Laptop Stand'),
(104, 5, 'Keyboard');CREATE TABLE defines each table's columns and their data types, INTEGER for whole numbers and TEXT for words. PRIMARY KEY marks the column that uniquely identifies each row. INSERT INTO adds the sample rows used throughout this guide, so once you run this, every JOIN query above will work exactly as shown.
They are the same thing. Writing JOIN by itself defaults to INNER JOIN in every major database. Many people write INNER JOIN explicitly anyway, because it makes the query easier to read and tells anyone reviewing the code exactly what kind of join is happening.
Yes. You can chain as many JOIN clauses as you need, one after another, each with its own ON condition. For example, you could join customers to orders, then join orders to a products table, to pull in the product's price as well.
A self join is when you join a table to itself, using two different aliases for it. It is useful for comparing rows within the same table, such as finding employees and their managers when both are stored in one employees table.
For INNER JOIN, no, the result is the same either way. For LEFT JOIN and RIGHT JOIN, yes, because the table order decides which side keeps all of its rows. Swapping LEFT JOIN for RIGHT JOIN and swapping the table order gives you the same result.
JOIN combines columns from two tables side by side, based on a matching value, to make wider rows. UNION stacks the results of two queries on top of each other, combining rows from queries that return the same columns, to make a taller result. They solve different problems and are not interchangeable.
Want help designing a database schema or writing complex SQL queries for your product? Our team can help you get it right from day one.
Talk to our teamOccasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.
Keep reading
What is an index in SQL? A plain-English guide with copy-paste examples showing how indexes speed up queries and when you actually need one.
Learn JavaScript DOM manipulation for beginners: select elements, change text and styles, and handle clicks with simple, copy-paste code examples.
Let's build something
Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.