SQL Joins (Overview)
In SQL, a JOIN clause is used to combine rows from two or more tables based on a related column between them. Instead of storing all data in one massive table, relational databases split information into separate, logical tables; joins allow you to stitch that data back together when writing queries.
Basic Syntax Blueprint
To join tables, you use the JOIN keyword to name the incoming table and the ON keyword to declare the matching link (usually a Primary Key matching a Foreign Key).
SELECT table1.column1, table2.column2
FROM table1
INNER JOIN table2 ON table1.common_column = table2.common_column;
Pro-Tip: Always use Table Aliases (like c for customers and o for orders) to keep your code clean and prevent you from typing out massive table names repeatedly.
The 4 Main Types of SQL Joins
| Join Type | Visual Rule / Logic | What it Returns |
|---|---|---|
| INNER JOIN | Intersection ($\cap$) | Only rows that have matching values in both tables. |
| LEFT JOIN (or LEFT OUTER JOIN) | Left Table + Matches | All rows from the left table, plus matching records from the right table. (Missing right values show as NULL). |
| RIGHT JOIN (or RIGHT OUTER JOIN) | Right Table + Matches | All rows from the right table, plus matching records from the left table. (Missing left values show as NULL). |
| FULL JOIN (or FULL OUTER JOIN) | Union ($\cup$) | All rows from both tables, matching them up where possible. (Unmatched sides show as NULL). |
Code Examples & Explanations
To demonstrate these joins, assume we have two tables connected by an ID:
- customers table (columns: customer_id, customer_name)
- orders table (columns: order_id, customer_id, order_amount)
INNER JOIN (The Default)
Returns only the customers who have actually placed an order. If a customer hasn't bought anything, they are completely excluded.
SELECT customers.customer_name, orders.order_amount
FROM customers
INNER JOIN orders ON customers.customer_id = orders.customer_id;
LEFT JOIN
Returns every single customer in your database, regardless of their shopping history. If they have placed an order, you see the amount. If they haven't, the order_amount column simply displays NULL.
SELECT customers.customer_name, orders.order_amount
FROM customers
LEFT JOIN orders ON customers.customer_id = orders.customer_id;
RIGHT JOIN
Returns every single order recorded in the system, along with the name of the buyer. If an order exists without a valid customer mapping (e.g., an anonymous guest checkout or data error), the customer_name displays as NULL.
SELECT customers.customer_name, orders.order_amount
FROM customers
RIGHT JOIN orders ON customers.customer_id = orders.customer_id;
FULL OUTER JOIN
Combines the logic of both Left and Right joins. It returns all customers and all orders, blending them where the IDs align, and padding unmatched data fields with NULL values.
SELECT customers.customer_name, orders.order_amount
FROM customers
FULL OUTER JOIN orders ON customers.customer_id = orders.customer_id;
Other Advanced Join Types
- SELF JOIN: Joining a table to itself. This is useful for hierarchical data inside a single table (e.g., matching an employee_id column to a manager_id column within the same staff table).
- CROSS JOIN: Creates a Cartesian product. It pairs every single row of the first table with every single row of the second table (no ON clause is used).
A Common Gotcha: Ambiguous Column ErrorsIf you try to select a column that exists in both tables (like customer_id) without specifying which table it should come from, the database engine will stop execution and throw an "Ambiguous Column Name" error.
-- ❌ THIS WILL FAIL:
SELECT customer_id, customer_name FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id;
-- THIS IS CORRECT (Explicitly naming 'c.customer_id'):
SELECT c.customer_id, c.customer_name FROM customers AS c INNER JOIN orders AS o ON c.customer_id = o.customer_id;
More Examples:
Imagine you have two simple tables:
- customers (with a customer_id and customer_name)
- orders (with an order_id, customer_id, and amount)
Example A: Using INNER JOIN (Find active transactional users)
This query pulls only the customers who have actually placed an order. Customers who haven't bought anything are filtered out.
SELECT c.customer_name, o.order_id, o.amount
FROM customers AS c
INNER JOIN orders AS o ON c.customer_id = o.customer_id;
Example B: Using LEFT JOIN (Find all users + their order histories)
This query extracts every single customer in your system. If a customer has zero orders, their name will still appear, but the order_id and amount columns will show up as blank NULL markers.
SELECT c.customer_name, o.order_id, o.amount
FROM customers AS c
LEFT JOIN orders AS o ON c.customer_id = o.customer_id;