JOIN Master
Tables
categories — Categories (parent-child hierarchy)
customers — Customers (grade, points, channel)
orders — Orders (status, amount, date)
order_items — Order items (qty, unit price)
products — Products (name, price, stock, brand)
reviews — Reviews (rating, content)
shipping — Shipping (carrier, tracking, status)
staff — Staff (dept, role, manager)
suppliers — Suppliers (company, contact)
tags — Tags (name, category)
product_tags — Product-tag mapping
Concepts
INNER JOIN, LEFT JOIN, multi-table JOIN, GROUP BY, aggregate functions
1. Retrieve each product's name, price, and category name. Top
Retrieve each product's name, price, and category name. Top 10 by price descending.
Hint 1: INNER JOIN products and categories on category_id, then ORDER BY ... DESC LIMIT 10.
Answer
SELECT p.name, p.price, cat.name AS category
FROM products AS p
INNER JOIN categories AS cat ON p.category_id = cat.id
ORDER BY p.price DESC
LIMIT 10;
Result (top 7 of 10 rows)
| name | price | category |
|---|---|---|
| MacBook Air 15 M3 Silver | 5,481,100.00 | MacBook |
| ASUS TUF Gaming RTX 5080 White | 4,526,600.00 | NVIDIA |
| ASUS Dual RTX 5070 Ti [Special Limite... | 4,496,700.00 | NVIDIA |
| Razer Blade 18 Black | 4,353,100.00 | Gaming Laptop |
| Razer Blade 16 Silver | 3,702,900.00 | Gaming Laptop |
| ASUS ROG Strix G16CH White | 3,671,500.00 | Custom Build |
| ASUS ROG Zephyrus G16 | 3,429,900.00 | Gaming Laptop |
2. Retrieve the product name, category name, and supplier compa
Retrieve the product name, category name, and supplier company name together.
Hint 1: INNER JOIN both categories and suppliers from products.
Answer
SELECT
p.name AS product,
cat.name AS category,
s.company_name AS supplier
FROM products AS p
INNER JOIN categories AS cat ON p.category_id = cat.id
INNER JOIN suppliers AS s ON p.supplier_id = s.id
ORDER BY p.name
LIMIT 20;
Result (top 7 of 20 rows)
| product | category | supplier |
|---|---|---|
| AMD Ryzen 9 9900X | AMD | AMD Corp. |
| AMD Ryzen 9 9900X | AMD | AMD Corp. |
| APC Back-UPS Pro Gaming BGM1500B Black | UPS/Power | APC Corp. |
| ASRock B850M Pro RS Black | AMD Socket | ASRock Corp. |
| ASRock B850M Pro RS Silver | AMD Socket | ASRock Corp. |
| ASRock B850M Pro RS White | AMD Socket | ASRock Corp. |
| ASRock B860M Pro RS Silver | Intel Socket | ASRock Corp. |
3. Retrieve the name and price of products that have never rece
Retrieve the name and price of products that have never received a review.
Hint 1: After LEFT JOIN reviews, use WHERE r.id IS NULL to find unmatched rows.
Answer
SELECT p.name, p.price
FROM products AS p
LEFT JOIN reviews AS r ON p.id = r.product_id
WHERE r.id IS NULL
ORDER BY p.price DESC;
Result (2 rows)
| name | price |
|---|---|
| MSI Radeon RX 9070 XT GAMING X | 1,896,000.00 |
| Hansung BossMonster DX5800 Black | 1,129,400.00 |
4. Retrieve the name and signup date of customers who have neve
Retrieve the name and signup date of customers who have never placed an order.
Hint 1: customers LEFT JOIN orders, then WHERE o.id IS NULL to filter customers with no orders.
Answer
SELECT c.name, c.created_at
FROM customers AS c
LEFT JOIN orders AS o ON c.id = o.customer_id
WHERE o.id IS NULL
ORDER BY c.created_at;
Result (top 7 of 2,391 rows)
| name | created_at |
|---|---|
| Alan Blair | 2016-01-03 19:49:46 |
| Dana Miles | 2016-01-15 19:21:20 |
| Tracy Johnson | 2016-01-26 09:42:20 |
| Tommy Kim | 2016-02-03 03:40:29 |
| Sara Harvey | 2016-02-03 04:18:52 |
| Duane Evans MD | 2016-02-09 18:54:54 |
| Ashley Jones | 2016-02-17 13:41:08 |
5. Retrieve each customer's name, grade, order count, and total
Retrieve each customer's name, grade, order count, and total amount spent. Top 10 by total spent.
Hint 1: customers JOIN orders, then aggregate with GROUP BY. Use COUNT and SUM together.
Answer
SELECT
c.name, c.grade,
COUNT(o.id) AS order_count,
ROUND(SUM(o.total_amount), 2) AS total_spent
FROM customers AS c
INNER JOIN orders AS o ON c.id = o.customer_id
WHERE o.status NOT IN ('cancelled')
GROUP BY c.id, c.name, c.grade
ORDER BY total_spent DESC
LIMIT 10;
Result (top 7 of 10 rows)
| name | grade | order_count | total_spent |
|---|---|---|---|
| Allen Snyder | VIP | 312 | 409,734,279.00 |
| Jason Rivera | VIP | 352 | 382,314,874.00 |
| Ronald Arellano | VIP | 225 | 266,184,349.00 |
| Brenda Garcia | VIP | 250 | 254,525,838.00 |
| Courtney Huff | VIP | 226 | 248,498,783.00 |
| Gabriel Walters | VIP | 290 | 248,168,491.00 |
| James Banks | VIP | 236 | 244,859,844.00 |
6. Retrieve the order number, customer name, product name, quan
Retrieve the order number, customer name, product name, quantity, and unit price for the 5 most recent orders.
Hint 1: Connect 4 tables with INNER JOIN: orders -> customers, orders -> order_items -> products.
Answer
SELECT
o.order_number,
c.name AS customer,
p.name AS product,
oi.quantity,
oi.unit_price
FROM orders AS o
INNER JOIN customers AS c ON o.customer_id = c.id
INNER JOIN order_items AS oi ON o.id = oi.order_id
INNER JOIN products AS p ON oi.product_id = p.id
ORDER BY o.ordered_at DESC
LIMIT 5;
Result (5 rows)
| order_number | customer | product | quantity | unit_price |
|---|---|---|---|---|
| ORD-20251231-37555 | Angel Jones | Norton AntiVirus Plus Silver | 1 | 74,800.00 |
| ORD-20251231-37543 | Carla Watson | Hancom Office 2024 Enterprise White | 1 | 134,100.00 |
| ORD-20251231-37552 | Martin Hanson | Hancom Office 2024 Enterprise White | 1 | 134,100.00 |
| ORD-20251231-37552 | Martin Hanson | NZXT Kraken 240 Silver | 1 | 120,200.00 |
| ORD-20251231-37548 | Lucas Johnson | Samsung 990 EVO Plus 1TB White | 1 | 187,700.00 |
7. Find the total revenue and units sold per category. Exclude
Find the total revenue and units sold per category. Exclude cancelled orders.
Hint 1: JOIN order_items -> products -> categories and exclude cancelled orders with WHERE o.status NOT IN ('cancelled').
Answer
SELECT
cat.name AS category,
SUM(oi.quantity) AS units_sold,
ROUND(SUM(oi.quantity * oi.unit_price), 2) AS revenue
FROM order_items AS oi
INNER JOIN orders AS o ON oi.order_id = o.id
INNER JOIN products AS p ON oi.product_id = p.id
INNER JOIN categories AS cat ON p.category_id = cat.id
WHERE o.status NOT IN ('cancelled')
GROUP BY cat.name
ORDER BY revenue DESC;
Result (top 7 of 38 rows)
| category | units_sold | revenue |
|---|---|---|
| Gaming Laptop | 1691 | 4,982,099,000.00 |
| AMD | 4016 | 3,124,984,300.00 |
| NVIDIA | 1661 | 2,814,694,400.00 |
| Gaming Monitor | 2464 | 2,781,055,700.00 |
| General Laptop | 1365 | 2,429,349,600.00 |
| 2-in-1 | 1301 | 1,944,050,200.00 |
| Intel Socket | 3406 | 1,556,580,900.00 |
8. Find the name, average rating, and review count of products
Find the name, average rating, and review count of products with 5 or more reviews.
Hint 1: products JOIN reviews, then aggregate with GROUP BY and filter with HAVING COUNT(r.id) >= 5.
Answer
SELECT
p.name,
ROUND(AVG(r.rating), 2) AS avg_rating,
COUNT(r.id) AS review_count
FROM products AS p
INNER JOIN reviews AS r ON p.id = r.product_id
GROUP BY p.id, p.name
HAVING COUNT(r.id) >= 5
ORDER BY avg_rating DESC
LIMIT 15;
Result (top 7 of 15 rows)
| name | avg_rating | review_count |
|---|---|---|
| Samsung DM500TDA Silver | 4.80 | 5 |
| LG 27UQ85R White | 4.60 | 5 |
| LG 32UN880 Ergo White | 4.56 | 16 |
| WD Elements 2TB Black | 4.53 | 19 |
| Windows 11 Home Black | 4.52 | 21 |
| Dell XPS Desktop 8960 Silver | 4.50 | 10 |
| Arctic Liquid Freezer III Pro 420 A-R... | 4.45 | 22 |
9. Find the average number of days from order to delivery for c
Find the average number of days from order to delivery for completed shipments.
Hint 1: Calculate the date difference with JULIANDAY(delivered_at) - JULIANDAY(ordered_at). JOIN shipping with orders.
Answer
SELECT
ROUND(AVG(JULIANDAY(sh.delivered_at) - JULIANDAY(o.ordered_at)), 1) AS avg_delivery_days
FROM shipping AS sh
INNER JOIN orders AS o ON sh.order_id = o.id
WHERE sh.status = 'delivered'
AND sh.delivered_at IS NOT NULL;
Result (1 rows)
| avg_delivery_days |
|---|
| 4.50 |
10. Find the total shipments, delivered count, and delivery rate
Find the total shipments, delivered count, and delivery rate per carrier.
Hint 1: GROUP BY carrier with conditional aggregation using CASE WHEN status = 'delivered'. Delivery rate is 100.0 * delivered / total.
Answer
SELECT
carrier,
COUNT(*) AS total,
SUM(CASE WHEN status = 'delivered' THEN 1 ELSE 0 END) AS delivered,
ROUND(100.0 * SUM(CASE WHEN status = 'delivered' THEN 1 ELSE 0 END) / COUNT(*), 1) AS delivery_rate
FROM shipping
GROUP BY carrier
ORDER BY total DESC;
Result (5 rows)
| carrier | total | delivered | delivery_rate |
|---|---|---|---|
| FedEx | 10,507 | 10,198 | 97.10 |
| UPS | 8993 | 8729 | 97.10 |
| USPS | 7227 | 6990 | 96.70 |
| DHL | 5356 | 5184 | 96.80 |
| OnTrac | 3533 | 3417 | 96.70 |
11. Find the product count, average price, and highest price per
Find the product count, average price, and highest price per supplier.
Hint 1: suppliers JOIN products, then aggregate with GROUP BY. Use COUNT, AVG, and MAX functions.
Answer
SELECT
s.company_name,
COUNT(p.id) AS product_count,
ROUND(AVG(p.price), 2) AS avg_price,
ROUND(MAX(p.price), 2) AS max_price
FROM suppliers AS s
INNER JOIN products AS p ON s.id = p.supplier_id
GROUP BY s.id, s.company_name
ORDER BY product_count DESC;
Result (top 7 of 45 rows)
| company_name | product_count | avg_price | max_price |
|---|---|---|---|
| ASUS Corp. | 26 | 1,683,630.77 | 4,526,600.00 |
| Samsung Official Distribution | 25 | 616,008.00 | 1,833,000.00 |
| Logitech Corp. | 17 | 111,600.00 | 216,800.00 |
| MSI Corp. | 13 | 778,430.77 | 1,896,000.00 |
| Seorin Systech | 12 | 157,908.33 | 269,200.00 |
| LG Official Distribution | 11 | 1,346,836.36 | 1,828,800.00 |
| TP-Link Corp. | 11 | 128,763.64 | 344,000.00 |
12. Retrieve each customer's most recent order date and order am
Retrieve each customer's most recent order date and order amount. Sort by most recent order.
Hint 1: Use MAX(o.ordered_at) to get the most recent order date and aggregate per customer with GROUP BY.
Answer
SELECT
c.name,
c.grade,
MAX(o.ordered_at) AS last_order_date,
o.total_amount AS last_order_amount
FROM customers AS c
INNER JOIN orders AS o ON c.id = o.customer_id
WHERE o.status NOT IN ('cancelled')
GROUP BY c.id, c.name, c.grade
ORDER BY last_order_date DESC
LIMIT 15;
Result (top 7 of 15 rows)
| name | grade | last_order_date | last_order_amount |
|---|---|---|---|
| Angel Jones | BRONZE | 2025-12-31 22:25:39 | 74,800.00 |
| Carla Watson | GOLD | 2025-12-31 21:40:27 | 134,100.00 |
| Martin Hanson | SILVER | 2025-12-31 20:00:48 | 254,300.00 |
| Lucas Johnson | BRONZE | 2025-12-31 18:43:56 | 187,700.00 |
| Adam Moore | BRONZE | 2025-12-31 18:00:24 | 155,700.00 |
| Justin Murphy | VIP | 2025-12-31 15:43:23 | 198,300.00 |
| Sara Hill | GOLD | 2025-12-31 15:33:05 | 335,000.00 |
13. Staff Hierarchy (Self-JOIN)
List all staff members with their manager names. Top-level staff with no manager should show NULL for manager name.
Hint 1: Self LEFT JOIN on staff table. staff AS s LEFT JOIN staff AS m ON s.manager_id = m.id. If no manager, m.name is NULL.
Answer
SELECT
s.id,
s.name AS staff_name,
s.department,
s.role,
m.name AS manager_name,
m.department AS manager_department
FROM staff AS s
LEFT JOIN staff AS m ON s.manager_id = m.id
ORDER BY s.department, s.name;
Result (5 rows)
| id | staff_name | department | role | manager_name | manager_department |
|---|---|---|---|---|---|
| 3 | Jonathan Smith | Management | admin | Michael Thomas | Management |
| 2 | Michael Mcguire | Management | admin | Michael Thomas | Management |
| 1 | Michael Thomas | Management | admin | NULL | NULL |
| 5 | Nicole Hamilton | Marketing | manager | Jonathan Smith | Management |
| 4 | Jaime Phelps | Sales | manager | Michael Thomas | Management |
14. Product Successor Chain (Self-JOIN)
Find discontinued products and their successors. Show discontinued product name, date, successor name, and successor price.
Hint 1: products.successor_id references id in the same table. products AS p JOIN products AS succ ON p.successor_id = succ.id. Filter p.discontinued_at IS NOT NULL.
Answer
SELECT
p.name AS discontinued_product,
p.price AS old_price,
p.discontinued_at,
succ.name AS successor_product,
succ.price AS new_price,
ROUND(succ.price - p.price, 0) AS price_diff
FROM products AS p
INNER JOIN products AS succ ON p.successor_id = succ.id
WHERE p.discontinued_at IS NOT NULL
ORDER BY p.discontinued_at DESC
LIMIT 20;
Result (top 7 of 18 rows)
| discontinued_product | old_price | discontinued_at | successor_product | new_price | price_diff |
|---|---|---|---|---|---|
| Dell XPS Desktop 8960 Silver | 1,249,400.00 | 2025-11-20 15:30:12 | HP Z2 Mini G1a Black | 895,000.00 | -354,400.00 |
| Hansung BossMonster DX7700 White | 1,579,400.00 | 2025-10-25 03:47:01 | Jooyon Rionine i9 High-End | 1,849,900.00 | 270,500.00 |
| SAPPHIRE PULSE RX 7800 XT Silver | 1,146,300.00 | 2025-08-01 06:10:51 | MSI Radeon RX 9070 XT GAMING X | 1,896,000.00 | 749,700.00 |
| Logitech G715 | 187,900.00 | 2025-04-16 06:47:20 | Ducky One 3 Full Black | 153,900.00 | -34,000.00 |
| Razer Basilisk V3 Pro 35K White | 102,100.00 | 2025-02-14 06:48:19 | Logitech G PRO X SUPERLIGHT 2 White | 120,400.00 | 18,300.00 |
| Canon imageCLASS MF655Cdw Black | 278,900.00 | 2024-09-20 15:47:07 | Epson L15160 | 1,019,500.00 | 740,600.00 |
| be quiet! Straight Power 12 1000W | 131,800.00 | 2024-08-15 23:34:23 | be quiet! Dark Power 13 1000W | 293,000.00 | 161,200.00 |
15. Q&A Thread View (Self-JOIN)
Show Q&A pairs in one row: question content, customer name, answer content, staff name.
Hint 1: Questions: parent_id IS NULL. Answers: parent_id references question's id. Question (q) LEFT JOIN Answer (a) ON a.parent_id = q.id.
Answer
SELECT
p.name AS product_name,
q.content AS question,
c.name AS asked_by,
q.created_at AS asked_at,
a.content AS answer,
s.name AS answered_by,
a.created_at AS answered_at
FROM product_qna AS q
INNER JOIN products AS p ON q.product_id = p.id
LEFT JOIN customers AS c ON q.customer_id = c.id
LEFT JOIN product_qna AS a ON a.parent_id = q.id
LEFT JOIN staff AS s ON a.staff_id = s.id
WHERE q.parent_id IS NULL
ORDER BY q.created_at DESC
LIMIT 20;
Result (top 7 of 20 rows)
| product_name | question | asked_by | asked_at | answer | answered_by | answered_at |
|---|---|---|---|---|---|---|
| SK hynix Platinum P41 2TB Silver | What are the exact dimensions? | Robert Simmons | 2025-12-30 23:10:22 | NULL | NULL | NULL |
| ASRock B850M Pro RS White | What PSU wattage do you recommend for... | Kathleen Stewart | 2025-12-30 23:01:05 | NULL | NULL | NULL |
| Dell U2724D | What PSU wattage do you recommend for... | Jill Reed | 2025-12-30 17:53:24 | Yes, it works with both Windows and Mac. | Jaime Phelps | 2025-12-30 20:53:24 |
| ASRock X870E Taichi Silver | Is this product new or refurbished? | Cory Salazar | 2025-12-30 14:27:52 | NULL | NULL | NULL |
| Fractal Design North | What are the exact dimensions? | Daniel Murphy | 2025-12-30 13:30:22 | NULL | NULL | NULL |
| MSI MEG Ai1300P PCIE5 White | Does this come with cables included? | Tracey Johnston | 2025-12-29 19:22:36 | Yes, all necessary cables are include... | Nicole Hamilton | 2025-12-30 21:22:36 |
| TP-Link TG-3468 Black | Can I use this with a Mac? | John Moss | 2025-12-29 10:01:52 | We expect restock within 2 weeks. You... | Nicole Hamilton | 2025-12-29 11:01:52 |
16. Days with No Orders (CROSS JOIN)
Using the calendar table, find dates in 2024 with zero orders.
Hint 1: LEFT JOIN calendar with orders on date. Extract date from ordered_at with SUBSTR(ordered_at, 1, 10). Filter WHERE o.id IS NULL for no-order days.
Answer
17. Product Tag Search (M:N JOIN)
Find products with the "Gaming" tag. Show product name, brand, price, and category.
Hint 1: JOIN product_tags -> tags to filter by tag name. JOIN product_tags -> products for product info. tags.name = 'Gaming'.
Answer
SELECT
p.name AS product_name,
p.brand,
p.price,
cat.name AS category
FROM product_tags AS pt
INNER JOIN tags AS t ON pt.tag_id = t.id
INNER JOIN products AS p ON pt.product_id = p.id
INNER JOIN categories AS cat ON p.category_id = cat.id
WHERE t.name = 'Gaming'
AND p.is_active = 1
ORDER BY p.price DESC;
Result (top 7 of 39 rows)
| product_name | brand | price | category |
|---|---|---|---|
| Razer Blade 18 Black | Razer | 4,353,100.00 | Gaming Laptop |
| Razer Blade 16 Silver | Razer | 3,702,900.00 | Gaming Laptop |
| Razer Blade 18 Black | Razer | 2,987,500.00 | Gaming Laptop |
| Razer Blade 18 White | Razer | 2,483,600.00 | Gaming Laptop |
| ASUS ROG Strix Scar 16 | ASUS | 2,452,500.00 | Gaming Laptop |
| ASUS ROG Swift PG32UCDM Silver | ASUS | 1,890,300.00 | Gaming Monitor |
| ASUS ROG Strix G16CH Silver | ASUS | 1,879,100.00 | Custom Build |