Skip to content
Laviyx Learn

Try

SQL JOIN Cheat Sheet

Every JOIN type with diagrams, syntax, and when to use each one.

Updated

The JOIN types at a glance#

INNER JOIN of A and B: Only rows that match in both tables.AB
INNER JOIN
LEFT JOIN of A and B: Every row from the left table, plus matches from the right.AB
LEFT JOIN
RIGHT JOIN of A and B: Every row from the right table, plus matches from the left.AB
RIGHT JOIN
FULL OUTER JOIN of A and B: Every row from both tables, matched where possible.AB
FULL OUTER JOIN
JOINKeepsUnmatched rows
INNER JOIN (or JOIN)only matching pairsdropped from both sides
LEFT JOINevery row of the left (first) tableright columns become NULL
RIGHT JOINevery row of the right (second) tableleft columns become NULL
FULL OUTER JOINevery row of both tablesNULL on whichever side is missing
CROSS JOINevery combination(no matching at all)
self joina table joined to itself with two aliasesdepends on the join type used

Sample data used below#

customers: Asha (1), Ravi (2), Meera (3), John (4)

orders

order_idcustomer_idamount
1011250
1021120
1032560
104590

Meera and John have no orders; order 104 points to a customer that does not exist.

JoinRows returned
INNER3
LEFT5
RIGHT4
FULL OUTER6
CROSS16 (4 × 4)

INNER JOIN: only matches#

SQLRun
SELECT c.name, o.order_id
FROM customers c
INNER JOIN orders o ON o.customer_id = c.customer_id;
Output
name | order_id
-----+---------
Asha | 101
Asha | 102
Ravi | 103

LEFT JOIN: all rows from the left table#

SQLRun
SELECT c.name, o.order_id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id;
Output
name  | order_id
------+---------
Asha  | 101
Asha  | 102
Ravi  | 103
Meera | NULL
John  | NULL

RIGHT JOIN: all rows from the right table#

SQLRun
SELECT c.name, o.order_id
FROM customers c
RIGHT JOIN orders o ON o.customer_id = c.customer_id;
-- same rows as: FROM orders o LEFT JOIN customers c ON ...
Output
name | order_id
-----+---------
Asha | 101
Asha | 102
Ravi | 103
NULL | 104

FULL OUTER JOIN: everything#

SQLRun
SELECT c.name, o.order_id
FROM customers c
FULL OUTER JOIN orders o ON o.customer_id = c.customer_id;
Output
name  | order_id
------+---------
Asha  | 101
Asha  | 102
Ravi  | 103
Meera | NULL
John  | NULL
NULL  | 104

MySQL has no FULL OUTER JOIN. Emulate it:

SQLRun
SELECT c.name, o.order_id FROM customers c LEFT JOIN orders o ON o.customer_id = c.customer_id
UNION
SELECT c.name, o.order_id FROM customers c RIGHT JOIN orders o ON o.customer_id = c.customer_id;

Find rows with no match (anti-join)#

SQLRun
-- Customers who never ordered
SELECT c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL;

-- Same result, often clearer
SELECT c.name
FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);
Output
name
-----
Meera
John

Avoid NOT IN (SELECT customer_id FROM orders): one NULL in the subquery makes it return no rows.

Self join: a table joined to itself#

SQLRun
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
Output
employee | manager
---------+--------
Asha     | Priya
Ravi     | Meera
Meera    | NULL
John     | Priya
Priya    | NULL

To pair rows that share a value without duplicates, add AND a.id < b.id to the join condition.

CROSS JOIN: every combination#

SQL
SELECT s.size, c.color
FROM sizes s
CROSS JOIN colors c;     -- rows = rows(sizes) × rows(colors)

Joining three or more tables#

SQL
SELECT o.order_id, c.name, p.name AS product, oi.quantity
FROM orders o
JOIN customers c    ON c.customer_id = o.customer_id
JOIN order_items oi ON oi.order_id   = o.order_id
JOIN products p     ON p.product_id  = oi.product_id;

Each new table needs its own ON condition linking it to a table already in the query.

ON vs WHERE in a LEFT JOIN#

SQLRun
-- Condition in ON: every customer kept
SELECT c.name, o.order_id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id AND o.amount > 200;
Output
name  | order_id
------+---------
Asha  | 101
Ravi  | 103
Meera | NULL
John  | NULL
SQLRun
-- Condition in WHERE: unmatched customers disappear
SELECT c.name, o.order_id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.amount > 200;
Output
name | order_id
-----+---------
Asha | 101
Ravi | 103

Rule: in a LEFT JOIN, filters on the right table go in ON; filters on the left table go in WHERE.

Traps to avoid#

TrapWhat happensFix
Missing or wrong ONaccidental cross join, huge row countsjoin foreign key to primary key
Ambiguous column (name in both tables)errorprefix with the alias: c.name
SUM after joining a "many" tableamounts counted once per child row (fan-out)aggregate in a subquery/CTE first
COUNT(*) after a LEFT JOINunmatched rows count as 1COUNT(right_table.id)
INNER JOIN after a LEFT JOIN on the same branchunmatched rows dropped againkeep the branch as LEFT JOIN

Full lessons: Introduction to JOINs, INNER, LEFT, RIGHT, FULL OUTER, SELF, CROSS, Multiple JOINs.

Want the full explanations? Go to the Learn SQL course →