MySQL JOIN
Combine rows from multiple tables with MySQL JOINs. Covers every JOIN type with examples.
How JOINs work
A JOIN combines rows from two or more tables based on a related column. Instead of storing everything in one large table, relational databases split data into separate tables and use JOINs to bring it back together at query time.
The basic pattern is:
The example schema
All examples on this page use three tables: customers, orders, and products.
Notice that customers 4 and 5 have no orders. This distinction will matter for LEFT JOIN.
INNER JOIN
INNER JOIN returns only rows where the ON condition matches in both tables. It is the default JOIN type, so JOIN and INNER JOIN are equivalent.
Expected output:
| name | order_id | order_date | |------|----------|------------| | Alice Nguyen | 101 | 2025-11-01 | | Alice Nguyen | 102 | 2025-11-08 | | Bob Okafor | 103 | 2025-11-10 | | Carol Santos | 104 | 2025-11-12 | | Carol Santos | 105 | 2025-11-15 |
Dana and Evan are absent because they have no matching rows in orders.
LEFT JOIN
LEFT JOIN returns all rows from the left table, plus matched rows from the right table. Where there is no match, the right-table columns come back as NULL.
Use this when you want every row from the left table regardless of whether a match exists.
Expected output:
| name | order_id | |------|----------| | Alice Nguyen | 101 | | Alice Nguyen | 102 | | Bob Okafor | 103 | | Carol Santos | 104 | | Carol Santos | 105 | | Dana Kim | NULL | | Evan Park | NULL |
Dana and Evan now appear, with NULL for order_id. To find customers who have never placed an order, filter on the NULL:
RIGHT JOIN
RIGHT JOIN is the mirror of LEFT JOIN. It returns all rows from the right table, plus matched rows from the left.
This produces the same result as the LEFT JOIN example above. In practice, RIGHT JOIN is rarely used because you can always rewrite it as a LEFT JOIN by swapping the table order. Most teams pick one convention (usually LEFT JOIN) and stick to it for readability.
Joining on multiple conditions
Add conditions to the ON clause with AND when a single column is not enough to define the relationship, or when you want to filter at join time rather than in WHERE.
Filtering in ON vs WHERE behaves differently for outer joins. For INNER JOIN the results are equivalent, but for LEFT JOIN an ON filter keeps unmatched rows (with NULLs), while a WHERE filter removes them.
Joining more than two tables
Chain additional JOINs to bring in more tables. Each JOIN operates on the result set produced so far.
Expected output:
| name | order_date | product | quantity | |------|------------|---------|----------| | Alice Nguyen | 2025-11-01 | Mechanical Keyboard | 1 | | Alice Nguyen | 2025-11-08 | USB-C Hub | 2 | | Bob Okafor | 2025-11-10 | Wireless Mouse | 1 | | Carol Santos | 2025-11-12 | Monitor Stand | 1 | | Carol Santos | 2025-11-15 | Wireless Mouse | 2 |
Self JOIN
A self JOIN joins a table to itself. The most common use case is a table where a row can reference another row in the same table, such as an employees table with a manager_id column.
To list each employee alongside their manager's name:
You must use aliases (e and m) because MySQL needs to distinguish between the two references to the same table. LEFT JOIN is used here so that the top-level manager (who has no manager) still appears in the results.
CROSS JOIN
CROSS JOIN returns every combination of rows from two tables (the cartesian product). A 5-row table crossed with a 4-row table produces 20 rows.
This is intentional when you need all combinations, for example generating a schedule grid, pairing every size with every color, or creating test data. In most other queries, a CROSS JOIN appearing by accident is a bug.
Common mistakes
Missing ON clause
Forgetting the ON clause in a regular JOIN produces an accidental cartesian product:
Always include an explicit ON condition.
Ambiguous column names
When two joined tables share a column name, MySQL cannot tell which one you mean:
Prefix ambiguous columns with the table name or alias:
Joining on NULL
NULL does not equal NULL in SQL. If a column used in the ON clause contains NULL, those rows will never match:
If NULLs are valid and should match, use <=> (the NULL-safe equality operator in MySQL) or filter/coalesce before joining.
Quick reference
| JOIN type | Returns |
|-----------|---------|
| INNER JOIN | Rows with a match in both tables |
| LEFT JOIN | All rows from the left table, NULLs where no match on the right |
| RIGHT JOIN | All rows from the right table, NULLs where no match on the left |
| CROSS JOIN | Every combination of rows from both tables |
| Self JOIN | A table joined to itself using aliases |