By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
(A Hyper-Practical, Zero-Fluff Study Guide)
You’re analyzing customer behavior for an e-commerce company. Your data is spread across: - customers (user IDs, names, signup dates) - orders (order IDs, user IDs, order dates, totals) - order_items (item IDs, order IDs, product IDs, quantities) - products (product IDs, names, categories, prices)
customers
orders
order_items
products
Problem: You need to answer: "Which product categories do high-value customers (spending > $500) buy most frequently?"
Without joins, you’d manually stitch CSV exports in Excel—slow, error-prone, and unscalable.With joins, you write a single SQL query that: 1. Links customers → orders → order_items → products.2. Filters for high-value customers.3. Aggregates by category.
Why this matters in production:- Performance: A poorly written join can scan millions of rows unnecessarily, slowing dashboards to a crawl.- Accuracy: Ambiguous column names (e.g., id in multiple tables) cause silent errors—your query runs but returns garbage.- Maintainability: Future analysts (or future you) will curse past-you if joins are a tangled mess.
id
Real-world scenario:You inherit a legacy BI dashboard that’s timing out. The root cause? A 10-table join with no WHERE clause, scanning 50M rows. Fixing it saves $2K/month in cloud compute costs.
WHERE
AS
FROM customers c
customers.customer_id
c.customer_id
INNER
NULL
LEFT JOIN
ON
USING
ON customers.id = orders.customer_id
ON a.id = b.parent_id AND b.status = 'active'
USING (customer_id)
ERROR: column reference "id" is ambiguous
employees
JOIN (SELECT ... FROM ...) AS subquery
FROM a JOIN b JOIN c
FROM a JOIN c JOIN b
/*+ LEADING(a,b,c) */
JOIN
COALESCE()
COALESCE(order_total, 0)
-- Create tables CREATE TABLE customers ( customer_id INT PRIMARY KEY, name VARCHAR(100), signup_date DATE ); CREATE TABLE orders ( order_id INT PRIMARY KEY, customer_id INT, order_date DATE, total DECIMAL(10, 2), FOREIGN KEY (customer_id) REFERENCES customers(customer_id) ); CREATE TABLE order_items ( item_id INT PRIMARY KEY, order_id INT, product_id INT, quantity INT, FOREIGN KEY (order_id) REFERENCES orders(order_id) ); CREATE TABLE products ( product_id INT PRIMARY KEY, name VARCHAR(100), category VARCHAR(50), price DECIMAL(10, 2) ); -- Insert sample data INSERT INTO customers VALUES (1, 'Alice', '2023-01-15'), (2, 'Bob', '2023-02-20'), (3, 'Charlie', '2023-03-10'); INSERT INTO orders VALUES (101, 1, '2023-04-01', 150.00), (102, 1, '2023-04-15', 300.00), (103, 2, '2023-04-10', 50.00), (104, 3, '2023-05-01', 600.00); INSERT INTO order_items VALUES (1001, 101, 1, 2), (1002, 101, 2, 1), (1003, 102, 3, 1), (1004, 103, 1, 1), (1005, 104, 2, 3), (1006, 104, 4, 1); INSERT INTO products VALUES (1, 'Laptop', 'Electronics', 750.00), (2, 'Mouse', 'Electronics', 25.00), (3, 'Desk Chair', 'Furniture', 300.00), (4, 'Monitor', 'Electronics', 200.00);
Goal: Find which product categories high-value customers (total spend > $500) buy most frequently.
SELECT p.category, COUNT(*) AS order_count, SUM(oi.quantity) AS total_units FROM customers c INNER JOIN orders o ON c.customer_id = o.customer_id INNER JOIN order_items oi ON o.order_id = oi.order_id INNER JOIN products p ON oi.product_id = p.product_id WHERE o.total > 500 -- High-value customers (spent > $500 in a single order) GROUP BY p.category ORDER BY total_units DESC;
Expected result:
category | order_count | total_units ------------+-------------+------------ Electronics | 2 | 4 Furniture | 1 | 1
Problem: What if customers and orders both had a name column?
name
-- This would fail: SELECT name, category FROM customers c JOIN orders o ON c.customer_id = o.customer_id JOIN products p ON oi.product_id = p.product_id; -- ERROR: column reference "name" is ambiguous -- Fix: Qualify with table aliases SELECT c.name, p.category FROM customers c JOIN orders o ON c.customer_id = o.customer_id JOIN order_items oi ON o.order_id = oi.order_id JOIN products p ON oi.product_id = p.product_id;
-- Good: Filter first SELECT * FROM (SELECT * FROM a WHERE status = 'active') a JOIN b ON a.id = b.id; `` - Index join keys: Ensure columns used inONclauses are indexed (e.g.,customer_idinorders).- AvoidSELECT `:* Explicitly list columns to reduce I/O.
`` - Index join keys: Ensure columns used in
clauses are indexed (e.g.,
in
).- Avoid
c
o
sql -- Join customers to orders, then to items, then to products -- Only include orders > $500 (high-value customers)
customers × max_orders_per_customer
sql CREATE TEMP TABLE high_value_orders AS SELECT * FROM orders WHERE total > 500;
LIMIT 10
ISNULL()
FULL OUTER JOIN
INNER JOIN
"Which join returns all rows from the left table, even if there’s no match in the right table?"
Ambiguous column handling:
"What’s wrong with this query?" sql SELECT id, name FROM customers JOIN orders ON id = customer_id;
sql SELECT id, name FROM customers JOIN orders ON id = customer_id;
SELECT customers.id, name FROM ...
Self-join scenarios:
"Write a query to find employees who earn more than their managers."
sql SELECT e.name FROM employees e JOIN employees m ON e.manager_id = m.id WHERE e.salary > m.salary;
Performance traps:
sql SELECT * FROM a JOIN b ON a.id = b.id WHERE a.date > '2023-01-01';
a
sql SELECT * FROM (SELECT * FROM a WHERE date > '2023-01-01') a JOIN b ON a.id = b.id;
Challenge:Write a query to find customers who ordered the same product more than once. Return the customer name, product name, and order count.
Solution:
SELECT c.name AS customer_name, p.name AS product_name, COUNT(*) AS order_count FROM customers c JOIN orders o ON c.customer_id = o.customer_id JOIN order_items oi ON o.order_id = oi.order_id JOIN products p ON oi.product_id = p.product_id GROUP BY c.customer_id, c.name, p.product_id, p.name HAVING COUNT(*) > 1;
Why it works:- Joins all 4 tables to link customers to products.- Groups by customer and product to count orders.- HAVING COUNT(*) > 1 filters for repeat orders.
HAVING COUNT(*) > 1
SELECT * FROM a JOIN b ON a.id = b.id;
SELECT * FROM a LEFT JOIN b ON a.id = b.id;
SELECT * FROM a RIGHT JOIN b ON a.id = b.id;
SELECT * FROM a FULL OUTER JOIN b ON a.id = b.id;
SELECT * FROM employees e JOIN employees m ON e.manager_id = m.id;
SELECT * FROM a JOIN (SELECT * FROM b WHERE ...) sub ON a.id = sub.id;
SELECT a.id, b.name FROM a JOIN b ON a.id = b.id;
SELECT * FROM (SELECT * FROM a WHERE ...) a JOIN b ON ...;
SELECT COUNT(*) FROM a JOIN b ON ...;
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.