SQL Joins Explained With Examples (Inner, Left, Right, Full)
Learn INNER, LEFT, RIGHT, FULL, CROSS and SELF joins with two tiny tables, clear queries and the exact results you should expect.
- INNER JOIN: only rows that match in both tables.
- LEFT JOIN: all rows from the left table, plus matches (NULL if none).
- RIGHT JOIN: all rows from the right table, plus matches.
- FULL JOIN: all rows from both tables, matched where possible.
- Join on keys, use table aliases and index the join columns.
Real databases never keep everything in one table. Customers live in one table and orders in another. To answer “which customers placed orders this month?” you must combine them, and that is what SQL joins explained in this guide covers.
This guide to SQL joins explained keeps everything practical. Joins confuse many beginners because the same two tables give different results depending on the join type. Once you see them with small, concrete data, they become easy.
SQL joins explained: the sample tables
customers
| id | name |
|---|---|
| 1 | Asha |
| 2 | Ben |
| 3 | Chitra |
orders
| id | customer_id | total |
|---|---|---|
| 101 | 1 | 250 |
| 102 | 1 | 120 |
| 103 | 2 | 90 |
| 104 | 9 | 40 |
Chitra has no orders, and order 104 points to a customer (9) who does not exist. These two edge cases show the difference between the join types.
INNER JOIN
An INNER JOIN returns only rows where the condition matches in both tables. Rows without a partner are dropped.
SELECT c.name, o.id AS order_id, o.total
FROM customers c
INNER JOIN orders o ON o.customer_id = c.id;
Result: Asha (101), Asha (102), Ben (103). Chitra and order 104 are missing. Use it when you only care about related data, such as orders with their customer details. Plain JOIN means INNER JOIN.
LEFT JOIN
A LEFT JOIN keeps every row from the left table and adds matching rows from the right. Where there is no match, right-side columns are NULL.
SELECT c.name, o.id AS order_id, o.total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;
Result: Asha (101), Asha (102), Ben (103), Chitra (NULL, NULL). Chitra now appears.
Quick tip: To find customers who never ordered, filter for NULL on the right side: WHERE o.id IS NULL returns Chitra. Testers use this to find orphaned or missing records.
RIGHT JOIN and FULL JOIN
A RIGHT JOIN mirrors a LEFT JOIN and keeps every row from the right table. Most people swap the table order and use LEFT JOIN because it reads more naturally.
SELECT c.name, o.id AS order_id
FROM customers c
RIGHT JOIN orders o ON o.customer_id = c.id;
The result includes order 104 with a NULL customer name. A FULL (OUTER) JOIN returns all rows from both tables: Chitra with no order and order 104 with no customer.
SELECT c.name, o.id AS order_id
FROM customers c
FULL OUTER JOIN orders o ON o.customer_id = c.id;
PostgreSQL and SQL Server support it. MySQL does not, so combine a LEFT JOIN and a RIGHT JOIN with UNION. For more on SQL joins explained, see the PostgreSQL manual page on table expressions and joins, and its joins tutorial.
| Join | Left-only rows | Right-only rows | Matches |
|---|---|---|---|
| INNER | No | No | Yes |
| LEFT | Yes | No | Yes |
| RIGHT | No | Yes | Yes |
| FULL | Yes | Yes | Yes |
CROSS JOIN and SELF JOIN
A CROSS JOIN pairs every row of one table with every row of the other: three customers and four orders give twelve rows. It is rare, but useful for generating combinations such as test data for every size and colour.
SELECT c.name, o.id
FROM customers c
CROSS JOIN orders o;
A SELF JOIN joins a table to itself using aliases, for example to find each employee’s manager.
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
SQL joins explained: common mistakes and performance tips
- Missing ON clause creates a CROSS JOIN by accident and explodes row counts.
- Filtering the right table in WHERE after a LEFT JOIN turns it into an INNER JOIN; put the condition in the ON clause.
- Ambiguous column names: prefix columns with table aliases.
- Joining on non-unique columns multiplies rows and inflates totals.
- Index the columns used in joins, select only the columns you need and check slow queries with
EXPLAIN.
Why joins matter for testers
QA engineers use joins to verify that data created through the UI or an API landed correctly in the database, and to find orphan records. Pair this with our API testing with Postman guide. For the basics see the SQL tutorial for beginners, and for newer data stores read about vector databases. To run a database locally, see our Docker tutorial.
Final thoughts on SQL joins explained
SQL joins explained in one line: INNER keeps matches only, LEFT keeps everything on the left, RIGHT keeps everything on the right and FULL keeps all of it. Practise with two tiny tables until you can predict the result before running the query, then try joining three tables and combining joins with GROUP BY.
Frequently asked questions
What is the difference between INNER JOIN and LEFT JOIN?
INNER JOIN returns only matching rows from both tables. LEFT JOIN returns all rows from the left table and fills unmatched right-side columns with NULL.
Which SQL join is the fastest?
No join type is always fastest. Performance depends on indexes, table size and the query plan. Index join columns and check the execution plan.
Does MySQL support FULL OUTER JOIN?
No. In MySQL, combine a LEFT JOIN and a RIGHT JOIN with UNION to get the same result.
What is a self join used for?
A self join compares rows within the same table, such as finding each employee’s manager in a single employees table.
Why does my join return duplicate rows?
Duplicates usually appear when the join column is not unique in one of the tables. Check your keys or aggregate the data before joining.
Keep learning
Need help with testing, WordPress or SEO?
We help teams ship better software and grow their sites. Tell us what you are working on and we will suggest a clear next step.