SQL Basics — Exercises¶
Consolidation exercises for SELECT, WHERE, ORDER BY, LIMIT, and DISTINCT. These use the same customers, products, and orders tables from 05-basic-query-practice.
Level 1 — Warm-Up (SELECT, FROM, basic WHERE)¶
Exercise 1.1
Retrieve all columns from the customers table. How many rows does it contain?
Exercise 1.2
Retrieve product_name, category, and unit_price from the products table.
Exercise 1.3
Find all orders with status = 'completed'. Show order_id, customer_id, order_date.
Exercise 1.4
Find all customers from country = 'India'.
Exercise 1.5
Find all products with unit_price > 100.
Show answers
Level 2 — Core Skills (Multiple conditions, BETWEEN, IN, NULL, ORDER BY, LIMIT)¶
Exercise 2.1
Find all orders that are either 'pending' or 'cancelled'. Use IN.
Exercise 2.2
Find products priced between £30 and £100 (inclusive). Show product_name, category, unit_price sorted cheapest first.
Exercise 2.3
Find customers who signed up in 2023. Sort by signup_date most recent first.
Exercise 2.4
Find orders where no discount was applied (discount_pct IS NULL) and the order is completed. Show order_id, customer_id, unit_price.
Exercise 2.5 List the 5 most expensive products.
Exercise 2.6 How many unique countries are our customers from?
Exercise 2.7
Find orders where quantity >= 3 AND unit_price > 50. Sort by unit_price descending.
Show answers
-- 2.1
SELECT order_id, customer_id, status
FROM orders
WHERE status IN ('pending', 'cancelled');
-- 2.2
SELECT product_name, category, unit_price
FROM products
WHERE unit_price BETWEEN 30 AND 100
ORDER BY unit_price ASC;
-- 2.3
SELECT customer_id, first_name, last_name, signup_date
FROM customers
WHERE signup_date BETWEEN '2023-01-01' AND '2023-12-31'
ORDER BY signup_date DESC;
-- 2.4
SELECT order_id, customer_id, unit_price
FROM orders
WHERE discount_pct IS NULL
AND status = 'completed';
-- 2.5
SELECT product_name, category, unit_price
FROM products
ORDER BY unit_price DESC
LIMIT 5;
-- 2.6
SELECT COUNT(DISTINCT country) AS unique_countries
FROM customers;
-- 2.7
SELECT order_id, customer_id, quantity, unit_price
FROM orders
WHERE quantity >= 3
AND unit_price > 50
ORDER BY unit_price DESC;
Level 3 — Stretch (Complex conditions, calculated columns, combined clauses)¶
Exercise 3.1
Calculate the gross total and net total for each order. Gross total is quantity * unit_price. Net total applies the discount: quantity * unit_price * (1 - COALESCE(discount_pct, 0) / 100). Show orders sorted by net total descending.
Exercise 3.2
Find all orders placed in the first two weeks of January 2024 (Jan 1–14) that are either completed or pending, with a gross total above £100. Sort by order_date ascending.
Exercise 3.3 List all customers whose email contains the word 'gmail' AND who are from the UK or India. Sort alphabetically by last name.
Exercise 3.4 Find the top 3 most expensive products in the Electronics or Furniture category.
Exercise 3.5 — Analytical thinking Write a query that finds orders where: - The order status is NOT cancelled or refunded - AND the delivery date is still NULL (not yet delivered) - AND the order was placed more than 7 days ago (before 2024-01-11, assuming today is 2024-01-18)
These are potentially overdue orders. Sort by order_date ascending (oldest first).
Show answers
-- 3.1
SELECT
order_id,
customer_id,
quantity,
unit_price,
discount_pct,
quantity * unit_price AS gross_total,
quantity * unit_price * (1 - COALESCE(discount_pct, 0) / 100) AS net_total
FROM orders
ORDER BY net_total DESC;
-- 3.2
SELECT
order_id,
customer_id,
order_date,
status,
quantity * unit_price AS gross_total
FROM orders
WHERE order_date BETWEEN '2024-01-01' AND '2024-01-14'
AND status IN ('completed', 'pending')
AND quantity * unit_price > 100
ORDER BY order_date ASC;
-- 3.3
SELECT customer_id, first_name, last_name, email, country
FROM customers
WHERE email LIKE '%gmail%'
AND country IN ('UK', 'India')
ORDER BY last_name;
-- 3.4
SELECT product_name, category, unit_price
FROM products
WHERE category IN ('Electronics', 'Furniture')
ORDER BY unit_price DESC
LIMIT 3;
-- 3.5
SELECT
order_id,
customer_id,
order_date,
status
FROM orders
WHERE status NOT IN ('cancelled', 'refunded')
AND delivery_date IS NULL
AND order_date < '2024-01-11'
ORDER BY order_date ASC;
Reflection Questions¶
After completing the exercises, think through these:
-
In Exercise 3.2, why did you use
quantity * unit_price > 100in theWHEREclause instead of thegross_totalalias? (Hint: execution order.) -
In Exercise 3.5, what happens if you use
status <> 'cancelled' AND status <> 'refunded'instead ofNOT IN? Are they equivalent? -
You need to find orders placed "this month." How would you write this in a way that works without hardcoding the date?
- MySQL:
WHERE MONTH(order_date) = MONTH(CURDATE()) AND YEAR(order_date) = YEAR(CURDATE()) - PostgreSQL:
WHERE DATE_TRUNC('month', order_date) = DATE_TRUNC('month', CURRENT_DATE)
Mini Challenge — Put It All Together¶
Scenario: Your manager asks for a report of "high-priority orders to follow up on."
Define these as orders that:
- Were placed in January 2024
- Are from VIP customers (customer segment = 'VIP', use a join or subquery)
- Have not been delivered yet (delivery_date IS NULL)
- Are not cancelled or refunded
Return: order_id, customer_id, order_date, status, gross_total (quantity × unit_price)
Sort by order_date ascending (oldest first), and limit to 20 results.
Show answer
SELECT
o.order_id,
o.customer_id,
o.order_date,
o.status,
o.quantity * o.unit_price AS gross_total
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_date BETWEEN '2024-01-01' AND '2024-01-31'
AND c.segment = 'VIP'
AND o.delivery_date IS NULL
AND o.status NOT IN ('cancelled', 'refunded')
ORDER BY o.order_date ASC
LIMIT 20;