SQL Practice Marathon β 30 Mixed Problems
Time to consolidate. Thirty problems spanning every topic from Sessions 02β07 β try each one yourself before opening the solution.
How to Use This Marathon
- Predict before you run. Write your query, then write down the expected result before executing it.
- Time yourself. Easy = 1β2 min, Medium = 3β5 min, Hard = 5β8 min.
- Narrate out loud. This is exactly what you'll do in a live coding interview.
- Reveal and compare. If your answer differs, figure out why β not just the correct syntax.
Schema Reference
| Table | Columns |
|---|---|
customers | customer_id, customer_name, city, account_type, balance, join_date |
transactions | txn_id, customer_id, amount, txn_date |
employees | emp_id, emp_name, manager_id, dept |
Sample data (same as Sessions 02β07): 7 customers, 8 transactions, 5 employees.
Part A β SELECT & Filtering (Problems 1β5)
1. List the names of all customers who live in Mumbai. Easy
SELECT customer_name
FROM customers
WHERE city = 'Mumbai';2. List customers with a balance greater than 50,000. Easy
SELECT customer_name, balance
FROM customers
WHERE balance > 50000;3. List customers in Mumbai or Delhi who also have a balance over 20,000. Medium
SELECT customer_name, city, balance
FROM customers
WHERE (city = 'Mumbai' OR city = 'Delhi')
AND balance > 20000;4. List customers whose name starts with the letter 'A'. Easy
SELECT customer_name
FROM customers
WHERE customer_name LIKE 'A%';5. List customers who joined in 2023 or later, ordered by balance (highest first). Medium
SELECT customer_name, join_date, balance
FROM customers
WHERE join_date >= '2023-01-01'
ORDER BY balance DESC;Part B β String, Date & Numeric Functions (Problems 6β10)
6. Show all customer names in uppercase. Easy
SELECT UPPER(customer_name) AS name_upper
FROM customers;7. Show a single column combining name and city, e.g. "Aarav Sharma - Mumbai". Easy
SELECT CONCAT(customer_name, ' - ', city) AS customer_location
FROM customers;8. List each customer and the length of their name. Easy
SELECT customer_name, LENGTH(customer_name) AS name_length
FROM customers;9. Add a column classifying each balance as High (β₯100000), Medium (β₯30000), or Low. Medium
SELECT customer_name, balance,
CASE
WHEN balance >= 100000 THEN 'High'
WHEN balance >= 30000 THEN 'Medium'
ELSE 'Low'
END AS balance_category
FROM customers;10. Show each customer's join year and join month. Easy
-- MySQL style
SELECT customer_name, YEAR(join_date) AS yr, MONTH(join_date) AS mth
FROM customers;Part C β Aggregations, GROUP BY & HAVING (Problems 11β15)
11. Count the total number of customers. Easy
SELECT COUNT(*) AS total_customers
FROM customers;12. Count the number of distinct cities. Easy
SELECT COUNT(DISTINCT city) AS unique_cities
FROM customers;13. Find the total balance for each account type. Medium
SELECT account_type, SUM(balance) AS total_balance
FROM customers
GROUP BY account_type;14. Find the average balance per city, rounded to 0 decimals. Medium
SELECT city, ROUND(AVG(balance), 0) AS avg_balance
FROM customers
GROUP BY city;15. List cities that have more than one customer. Medium
SELECT city, COUNT(*) AS customer_count
FROM customers
GROUP BY city
HAVING COUNT(*) > 1;Part D β Joins (Problems 16β20)
16. List customer names with their transaction amounts (only customers with transactions). Medium
SELECT c.customer_name, t.amount
FROM customers c
INNER JOIN transactions t ON c.customer_id = t.customer_id;17. List ALL customers with their transaction amounts (NULL where none). Medium
SELECT c.customer_name, t.amount
FROM customers c
LEFT JOIN transactions t ON c.customer_id = t.customer_id;18. Find customers who have never made a transaction. Hard
SELECT c.customer_name
FROM customers c
LEFT JOIN transactions t ON c.customer_id = t.customer_id
WHERE t.txn_id IS NULL;19. Find the total spend per customer (including 0 for none). Medium
SELECT c.customer_name,
COALESCE(SUM(t.amount), 0) AS total_spent
FROM customers c
LEFT JOIN transactions t ON c.customer_id = t.customer_id
GROUP BY c.customer_name
ORDER BY total_spent DESC;20. List each employee with their manager's name (self join). Hard
SELECT e.emp_name AS employee, m.emp_name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.emp_id;Part E β Subqueries & CTEs (Problems 21β25)
21. List customers whose balance is above the overall average (subquery). Medium
SELECT customer_name, balance
FROM customers
WHERE balance > (SELECT AVG(balance) FROM customers);22. Find the second-highest balance (subquery). Hard
SELECT MAX(balance) AS second_highest
FROM customers
WHERE balance < (SELECT MAX(balance) FROM customers);23. Show each customer with their transaction count (correlated subquery). Hard
SELECT c.customer_name,
(SELECT COUNT(*) FROM transactions t
WHERE t.customer_id = c.customer_id) AS txn_count
FROM customers c;24. List customers with more than one transaction (CTE). Medium
WITH txn_counts AS (
SELECT customer_id, COUNT(*) AS no_of_txn
FROM transactions GROUP BY customer_id
)
SELECT c.customer_name
FROM customers c
JOIN txn_counts tc ON c.customer_id = tc.customer_id
WHERE tc.no_of_txn > 1;25. Find the average number of transactions per customer (CTE). Hard
WITH txn_counts AS (
SELECT customer_id, COUNT(*) AS no_of_txn
FROM transactions GROUP BY customer_id
)
SELECT AVG(no_of_txn) AS avg_txn_per_customer
FROM txn_counts;Part F β Window Functions (Problems 26β30)
26. Rank customers by balance (highest first) with ROW_NUMBER, RANK, and DENSE_RANK. Medium
SELECT customer_name, balance,
ROW_NUMBER() OVER (ORDER BY balance DESC) AS rn,
RANK() OVER (ORDER BY balance DESC) AS rk,
DENSE_RANK() OVER (ORDER BY balance DESC) AS dr
FROM customers;27. Find the top customer by balance in each city (top-N per group). Hard
SELECT city, customer_name, balance
FROM (
SELECT city, customer_name, balance,
ROW_NUMBER() OVER (PARTITION BY city ORDER BY balance DESC) AS rn
FROM customers
) t
WHERE rn = 1;28. Compute a running total of transaction amounts ordered by date. Hard
SELECT txn_date, amount,
SUM(amount) OVER (ORDER BY txn_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM transactions
ORDER BY txn_date;29. Show each transaction amount along with the previous transaction's amount (LAG). Hard
SELECT txn_id, amount,
LAG(amount) OVER (ORDER BY txn_date) AS prev_amount
FROM transactions
ORDER BY txn_date;30. Show each customer's balance as a percentage of their city's total. Hard
SELECT customer_name, city, balance,
ROUND(100.0 * balance / SUM(balance) OVER (PARTITION BY city), 1) AS pct_of_city
FROM customers;Interview Questions β The Live-Coding Classics
These are the SQL problems interviewers ask again and again. Each maps to problems you've already solved above β the value here is recognizing the pattern instantly.
IQ1. How do you approach a SQL live-coding question in an interview?
Model answer: "I don't jump to code. I restate the business question, name the table and columns I need, mention any edge case, then write the query and explain the output. Showing my reasoning matters more than a silent correct query."
IQ2. Find the second-highest salary.
Approach: max below the max, or DENSE_RANK = 2. See Problem 22 and the DENSE_RANK approach.
IQ3. How do you find duplicate rows in a table?
Approach: GROUP BY the identifying column + HAVING COUNT(*) > 1 (see Problem 15).
IQ4. Find the top N items per group.
Approach: ROW_NUMBER() OVER (PARTITION BY β¦ ORDER BY β¦) wrapped in a subquery, filter rn <= N (see Problem 27).
IQ5. Compute a running total / cumulative sum.
Approach: SUM(β¦) OVER (ORDER BY β¦ ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) (see Problem 28).
IQ6. Find customers with no orders.
Approach: LEFT JOIN β¦ WHERE right.key IS NULL β the anti-join (see Problem 18).
IQ7. Compute month-over-month growth.
Approach: aggregate by month, then LAG(revenue) OVER (ORDER BY month) (see Problem 29 pattern).
IQ8. Compute a ratio or percent-of-total.
Approach: divide the row value by SUM(β¦) OVER (PARTITION BY β¦) (see Problem 30).
Key Takeaways
Predict the output before running β that's the skill interviewers test.
Almost every problem is one of 8 patterns: filter, aggregate, join, anti-join, subquery, CTE, top-N, running total.
Narrate your thinking out loud β the framework scores more than raw syntax.
If stuck, break the problem into steps and consider a CTE.
Time-box yourself: easy β€2 min, medium β€5 min, hard β€8 min.
Objective Questions β Test Your Understanding
Q1. Which clause filters rows before grouping/aggregation?
Q2. Which join returns only the rows that match in both tables?
Q3. Which function returns the nth-highest distinct value cleanly (no skipped ranks)?
Q4. To find duplicate values in a column, you combine GROUP BY withβ¦
Q5. The "top N per group" pattern uses which combination?