Sample Data Used Throughout This Guide

Sample tables: customers and orders -- customers table id | name | city ---+-----------+--------- 1 | Alice | Delhi 2 | Bob | Mumbai 3 | Charlie | Pune 4 | Diana | Bangalore ← no orders -- orders table id | customer_id | amount ---+-------------+------- 101| 1 | 500 ← Alice's order 102| 1 | 300 ← Alice's order 103| 2 | 700 ← Bob's order 104| 5 | 200 ← customer_id 5 doesn't exist in customers

INNER JOIN

INNER JOIN returns rows where there is a match in BOTH tables. Non-matching rows from either table are excluded.

Customers: [Alice] [Bob] [Charlie] [Diana] Orders: [101] [102] [103] [104] INNER JOIN: Only overlapping rows — Diana excluded, order 104 excluded
INNER JOIN SELECT c.name, o.id AS order_id, o.amount FROM customers c INNER JOIN orders o ON c.id = o.customer_id; -- Result: name | order_id | amount ------+----------+------- Alice | 101 | 500 Alice | 102 | 300 Bob | 103 | 700 -- Charlie: no orders → excluded -- Diana: no orders → excluded -- Order 104: customer_id 5 not in customers → excluded

INNER JOIN is the Default

Writing just "JOIN" in SQL means INNER JOIN. They are identical. Most developers write "JOIN" for brevity; "INNER JOIN" is more explicit and readable in complex queries.

LEFT JOIN (LEFT OUTER JOIN)

LEFT JOIN returns ALL rows from the left table, plus matched rows from the right table. Where there is no match, right table columns are NULL.

Customers: [Alice] [Bob] [Charlie] [Diana] Orders: [101] [102] [103] [104] LEFT JOIN: ALL customers + matched orders; Diana gets NULL order columns
LEFT JOIN SELECT c.name, o.id AS order_id, o.amount FROM customers c LEFT JOIN orders o ON c.id = o.customer_id; -- Result: name | order_id | amount --------+----------+------- Alice | 101 | 500 Alice | 102 | 300 Bob | 103 | 700 Charlie | NULL | NULL ← no orders — NULL in order columns Diana | NULL | NULL ← no orders — NULL in order columns -- Find customers with NO orders: SELECT c.name FROM customers c LEFT JOIN orders o ON c.id = o.customer_id WHERE o.id IS NULL; -- Returns: Charlie, Diana

RIGHT JOIN (RIGHT OUTER JOIN)

RIGHT JOIN returns ALL rows from the right table, plus matched rows from the left table. Where there is no match, left table columns are NULL. Right JOIN is the mirror of LEFT JOIN.

RIGHT JOIN SELECT c.name, o.id AS order_id, o.amount FROM customers c RIGHT JOIN orders o ON c.id = o.customer_id; -- Result: name | order_id | amount ------+----------+------- Alice | 101 | 500 Alice | 102 | 300 Bob | 103 | 700 NULL | 104 | 200 ← order 104 has no matching customer — NULL for name

Prefer LEFT JOIN over RIGHT JOIN

RIGHT JOIN is the least commonly used JOIN. Most SQL developers prefer to rewrite RIGHT JOINs as LEFT JOINs by swapping the table order — it is easier to read and reason about when the "primary" table is always on the left.

FULL OUTER JOIN

FULL OUTER JOIN returns ALL rows from BOTH tables. Where there is no match on either side, NULLs fill the missing columns.

FULL OUTER JOIN (PostgreSQL) SELECT c.name, o.id AS order_id, o.amount FROM customers c FULL OUTER JOIN orders o ON c.id = o.customer_id; -- Result: name | order_id | amount --------+----------+------- Alice | 101 | 500 Alice | 102 | 300 Bob | 103 | 700 Charlie | NULL | NULL ← customer with no orders Diana | NULL | NULL ← customer with no orders NULL | 104 | 200 ← order with no matching customer -- MySQL workaround (no native FULL OUTER JOIN): SELECT c.name, o.id, o.amount FROM customers c LEFT JOIN orders o ON c.id = o.customer_id UNION SELECT c.name, o.id, o.amount FROM customers c RIGHT JOIN orders o ON c.id = o.customer_id;

CROSS JOIN

CROSS JOIN returns the Cartesian product — every combination of rows from both tables. No ON condition.

CROSS JOIN — generate combinations -- All colour × size combinations for a product SELECT colours.name, sizes.label FROM colours CROSS JOIN sizes; -- colours: Red, Blue, Green (3 rows) -- sizes: S, M, L, XL (4 rows) -- Result: 12 rows (all 3×4 combinations) -- Practical use: populate a time-series reporting table SELECT d.date, h.hour FROM dates d CROSS JOIN hours h;

SELF JOIN

A SELF JOIN joins a table to itself. Used for hierarchical or comparative data within the same table.

SELF JOIN — employee/manager hierarchy -- employees table has manager_id pointing to another employee row SELECT e.name AS employee, m.name AS manager FROM employees e LEFT JOIN employees m ON e.manager_id = m.id; -- employee | manager -- Alice | Bob (Alice reports to Bob) -- Bob | NULL (Bob has no manager — CEO) -- Charlie | Alice

JOIN Types Summary

JOIN TypeReturnsNULLs forCommon Use Case
INNER JOINMatching rows onlyNoneGet related data from both tables
LEFT JOINAll left + matched rightUnmatched right rowsAll records + optional related data
RIGHT JOINAll right + matched leftUnmatched left rowsRarely used — rewrite as LEFT JOIN
FULL OUTER JOINAll rows from bothUnmatched from either sideFind unmatched records on either side
CROSS JOINCartesian productNoneGenerate all combinations
SELF JOINTable joined to itselfDepends on JOIN typeHierarchies, comparisons within one table

Always Index Your JOIN Columns

The most common performance problem with JOINs is missing indexes on the ON condition columns. Always index foreign key columns and any column used in a JOIN condition. Run EXPLAIN before and after adding indexes to verify the query plan changes from Seq Scan to Index Scan.

How We Research and Update This Guide

We test the underlying formula or workflow, compare outputs with reliable references, and revise examples whenever the page content changes.

  • The workflow or formula is tested directly in the tool and compared against independent reference examples.
  • Examples are kept practical so readers can verify the result without hidden assumptions.
  • Pages are revised whenever the interface, calculation flow, or surrounding guidance materially changes.

Frequently Asked Questions — SQL JOINs