VillageSQL is a drop-in replacement for MySQL with extensions.
All examples in this guide work on VillageSQL. Install Now →
The Tables Used in This Guide
JOIN Types at a Glance
| Type | Returns | Use when |
|---|---|---|
INNER JOIN | Only rows with a match in both tables | You want results that exist on both sides |
LEFT JOIN | All rows from the left table; NULL for unmatched right rows | You want all left-side rows regardless of whether a match exists |
RIGHT JOIN | All rows from the right table; NULL for unmatched left rows | Same result as LEFT JOIN with tables swapped — most developers rewrite it that way |
CROSS JOIN | Every combination of left and right rows (Cartesian product) | Generating all possible pairs; no ON condition |
INNER JOIN — Only Matching Rows
INNER JOIN (or just JOIN) returns rows where the join condition matches in both tables. Rows with no match in either table are excluded.
customer_id = NULL is also excluded because NULL doesn’t match any value. Only rows with a match on both sides appear. For a full explanation of how MySQL handles NULL comparisons, see NULL in MySQL.
LEFT JOIN — All Rows from the Left Table
LEFT JOIN returns all rows from the left table, plus any matching rows from the right table. When there’s no match, the right-side columns are NULL.
NULL for amount. This is the right JOIN to use when you want all rows from the primary table, whether or not related rows exist.
Finding rows with no match — filter on a NULL right-side column:
RIGHT JOIN — All Rows from the Right Table
RIGHT JOIN is the mirror of LEFT JOIN: all rows from the right table, NULLs for unmatched left-side columns. In practice, most developers rewrite RIGHT JOIN as a LEFT JOIN with the tables swapped — it’s easier to read.
CROSS JOIN — Every Combination
CROSS JOIN produces the Cartesian product: every row from the left table paired with every row from the right table. With 3 customers and 4 orders, you get 12 rows. There’s no ON condition.
JOIN Performance
JOINs on large tables can be slow if the join columns aren’t indexed.EXPLAIN shows the join order and which indexes are used — look for type: ALL on any joined table as a sign that an index is missing.
Frequently Asked Questions
What’s the difference between JOIN and INNER JOIN?
Nothing. They’re the same. INNER JOIN is the explicit form; JOIN is shorthand. Both return only matching rows.
Why does adding a WHERE clause to a LEFT JOIN turn it into an INNER JOIN?
When you filter on a column from the right table in the WHERE clause, rows where that column is NULL (the unmatched rows) are excluded. Put the condition in the ON clause instead if you want to keep unmatched rows:
Does MySQL support FULL OUTER JOIN?
No — MySQL doesn’t haveFULL OUTER JOIN. Emulate it with a LEFT JOIN and RIGHT JOIN combined with UNION:
UNION deduplicates the result. If your data can have legitimately duplicate rows and you want to preserve them, use UNION ALL — but be aware it will also include rows that appear in both halves.
Troubleshooting
| Problem | Solution |
|---|---|
LEFT JOIN returns fewer rows than expected | A WHERE condition on the right table is filtering out NULL rows — move the condition to ON |
| JOIN on large tables is slow | Add an index on the join column of the larger table |
| Unexpected duplicate rows | The join is producing multiple matches — use GROUP BY or check whether the relationship is truly one-to-one |
RIGHT JOIN is confusing | Rewrite as LEFT JOIN with tables swapped — same result, easier to read |

