Free Interactive Course · SQL

SQL Mastery

Learn SQL by running it. Every example below is a real SQLite database living in your browser — edit any query and press Run. Nothing is sent to a server. Work top to bottom, or jump around with the contents on the left.

ShareXLinkedIn

The dataset

Every lesson uses the same tiny e-commerce database. Four tables:

customersid, name, country, signup_date
productsid, name, category, price
ordersid, customer_id, order_date, status
order_itemsid, order_id, product_id, quantity
01

SELECT, WHERE & ORDER BY

Every query starts with SELECT — you name the columns you want and the table they live in. Run this to see all customers:

SQL
⌘/Ctrl + Enter

WHERE filters rows. Only keep customers from India:

SQL
🔮Before you run — how many customers are from India?
⌘/Ctrl + Enter

ORDER BY sorts the result. Show the most recent signups first with DESC:

SQL
⌘/Ctrl + Enter
Your turn: edit the query above to show only customers who signed up in 2025, sorted by name. (Hint: WHERE signup_date >= '2025-01-01'.)
P
PriyaFounder · Kettle & Co.now
Morning! ☕ Investor call at 3 and I need a quick one: pull our customers from India, newest signups first. Just name and signup date, please. 🙏
🎯 Your mission Return name and signup_date for customers in India, most recent signup first.
SQL
⌘/Ctrl + Enter
02

Filtering: AND, OR, IN, BETWEEN, LIKE, NULL

Combine conditions with AND / OR. Customers from India who signed up in 2025:

SQL
🔮Now AND narrows it to India *and* a 2025 signup. How many survive?
⌘/Ctrl + Enter

IN matches a list; BETWEEN matches a range (inclusive):

SQL
🔮IN two categories *and* a price range. How many products match?
⌘/Ctrl + Enter

LIKE does pattern matching — % is any run of characters, _ is a single character. Names starting with 'A':

SQL
⌘/Ctrl + Enter
Your turn: find every product whose name contains the word Cable or Mug. (Two LIKEs joined by OR.)
P
PriyaFounder · Kettle & Co.now
We’re running a “budget tech” promo 🎉 Can you list every Electronics product that costs under $30? I just need the name and price.
🎯 Your mission Return name and price for products in the Electronics category priced below 30.
SQL
⌘/Ctrl + Enter
03

JOINs: Combining Tables

Data is split across tables to avoid duplication. A JOIN stitches them back together on a shared key. Match each order to the customer who placed it:

SQL
🔮Every order joins to its customer. How many rows come back?
⌘/Ctrl + Enter

An INNER JOIN (the default) keeps only rows that match on both sides. A LEFT JOIN keeps every row from the left table even when there is no match — perfect for finding gaps. Which customers have never ordered?

SQL
🔮This anti-join finds customers with NO orders. How many?
⌘/Ctrl + Enter
SQLite has no RIGHT or FULL OUTER JOIN — you get the same effect by swapping table order or using UNION. Your turn: join order_items to products to list every product name that was ordered.
P
PriyaFounder · Kettle & Co.now
I want to win back quiet customers. Which of our customers have never placed an order? Just give me their names and I’ll email them a coupon. 💌
🎯 Your mission Return the name of every customer who has no orders at all.
SQL
⌘/Ctrl + Enter
04

GROUP BY, Aggregations & HAVING

Aggregation functions (COUNT, SUM, AVG, MIN, MAX) collapse many rows into one summary. Count how many orders each status has:

SQL
🔮Orders collapse into one row per status. How many groups?
⌘/Ctrl + Enter

GROUP BY defines the buckets; the aggregate runs per bucket. Total revenue per product category (joining three tables):

SQL
⌘/Ctrl + Enter

Filter groups (not rows) with HAVING — it runs after aggregation, so it can reference the aggregate. Only categories with revenue over 100:

SQL
⌘/Ctrl + Enter
Rule of thumb: WHERE filters rows before grouping, HAVING filters groups after. Your turn: find each customer's total number of orders, highest first.
P
PriyaFounder · Kettle & Co.now
Board deck time 📊 I need our total revenue per product category, biggest earner at the top. Give me category and revenue (price × quantity summed).
🎯 Your mission Return category and total revenue (SUM of price × quantity), highest revenue first.
SQL
⌘/Ctrl + Enter
05

Subqueries

A subquery is a query nested inside another. A scalar subquery returns a single value you can compare against — products priced above the average:

SQL
🔮How many products cost more than the average price?
⌘/Ctrl + Enter

A subquery in IN returns a list. Customers who have placed at least one order:

SQL
⌘/Ctrl + Enter

A correlated subquery references the outer row and re-runs per row — here, an order count per customer:

SQL
⌘/Ctrl + Enter
Your turn: list products that have never appeared in order_items using NOT IN.
P
PriyaFounder · Kettle & Co.now
Thinking about our “premium” line. Which products cost more than our average price? Just the names, cheapest of those first — use a subquery for the average. 🧮
🎯 Your mission Return the name of every product priced above the average product price, ordered by price ascending.
SQL
⌘/Ctrl + Enter
06

CTEs: The WITH Clause

A Common Table Expression (CTE) names a subquery up front with WITH, so your main query reads cleanly. Compute each order's total, then keep only the big ones:

SQL
⌘/Ctrl + Enter

You can chain multiple CTEs, and even reference one from the next. CTEs can also be recursive — generate the numbers 1 to 5 with no table at all:

SQL
⌘/Ctrl + Enter
Your turn: extend the recursive CTE to count 1 to 20, then only show even numbers (WHERE n % 2 = 0 in the final SELECT).
P
PriyaFounder · Kettle & Co.now
Chasing our big baskets 🛒 Using a CTE, find the orders worth more than $40. I need the order id and its total, biggest first.
🎯 Your mission Using a CTE, return order_id and total (SUM of quantity × price) for orders totalling over 40, highest first.
SQL
⌘/Ctrl + Enter
07

Window Functions

A window function computes across a set of rows without collapsing them — you keep every row and add a calculated column. ROW_NUMBER() numbers rows inside each PARTITION:

SQL
⌘/Ctrl + Enter

RANK() ranks by a value across the whole set. Rank customers by total spend (CTE + window together):

SQL
⌘/Ctrl + Enter

Add ORDER BY inside the window to get a running total — cumulative spend per customer over time:

SQL
⌘/Ctrl + Enter
Your turn: swap RANK() for DENSE_RANK() and LAG(total) OVER (ORDER BY total DESC) to see the previous customer's spend on each row.
P
PriyaFounder · Kettle & Co.now
VIP program idea 🌟 Rank our customers by total spend — top spender = rank 1. I need name, their total, and the rank. (Customers who never ordered can drop off.)
🎯 Your mission Return name, total spend, and a spend RANK() (1 = highest) for every customer who has ordered.
SQL
⌘/Ctrl + Enter
08

CASE: Conditional Logic

CASE is SQL's if/else. Bucket products into price tiers:

SQL
⌘/Ctrl + Enter

CASE inside an aggregate is how you pivot rows into columns — count orders by status in a single row:

SQL
⌘/Ctrl + Enter
Your turn: add a CASE column labelling each customer 'domestic' when country = 'India' else 'international'.
P
PriyaFounder · Kettle & Co.now
For the pricing page I want to tag each product by tier. Call it 'premium' at $50+, 'mid' at $15+, otherwise 'budget'. Give me name and tier. 🏷️
🎯 Your mission Return each product’s name and a tier label: premium if price ≥ 50, mid if ≥ 15, else budget.
SQL
⌘/Ctrl + Enter
09

Set Operations: UNION, INTERSECT, EXCEPT

Set operators stack the results of two queries. UNION merges and de-duplicates; UNION ALL keeps duplicates and is faster:

SQL
⌘/Ctrl + Enter

EXCEPT returns rows in the first query that are not in the second — a clean way to find products that were never ordered:

SQL
🔮EXCEPT subtracts ordered products. How many were never ordered?
⌘/Ctrl + Enter
The two queries must return the same number of columns with compatible types. Your turn: use INTERSECT to list product ids that appear in order_items and cost more than 20.
P
PriyaFounder · Kettle & Co.now
Inventory clean-up 🧹 Which products have we never actually sold? Give me their ids. Bonus points if you use EXCEPT.
🎯 Your mission Return the id of every product that has never appeared in order_items. Use EXCEPT.
SQL
⌘/Ctrl + Enter
10

String & Text Functions

SQL can transform text inline. UPPER, LOWER, LENGTH, and SUBSTR are the everyday ones:

SQL
⌘/Ctrl + Enter

The || operator concatenates strings — great for building labels:

SQL
⌘/Ctrl + Enter
Your turn: use REPLACE(name, 'a', '@') and INSTR(name, 'a') to see substitution and position-finding in action.
P
PriyaFounder · Kettle & Co.now
Printing mailing labels 🏷️ Build me one label per customer in the form NAME (COUNTRY) — name in UPPERCASE. One column, call it label.
🎯 Your mission Return a single column label for each customer, formatted as UPPERCASE NAME (Country).
SQL
⌘/Ctrl + Enter
11

Date & Time Functions

Dates are stored as ISO text (YYYY-MM-DD), which sorts and compares correctly. STRFTIME pulls out parts of a date:

SQL
⌘/Ctrl + Enter

julianday() converts a date to a number of days, so subtracting two of them gives an age in days. How long ago (from 9 Jul 2025) was each order placed?

SQL
⌘/Ctrl + Enter
Your turn: count how many orders fell in each month using GROUP BY STRFTIME('%Y-%m', order_date).
P
PriyaFounder · Kettle & Co.now
Trying to spot our busy months 📈 How many orders did we get each month? Give me the month as YYYY-MM and the count.
🎯 Your mission Return the month (formatted YYYY-MM) and the number of orders in each month.
SQL
⌘/Ctrl + Enter
12

Modifying Data: INSERT, UPDATE, DELETE

These statements change the in-browser database. Changes persist across the cells on this page until you reload, which restores the original data. Nothing touches a real server.

INSERT adds rows. Add a new customer, then read it back:

SQL
⌘/Ctrl + Enter
SQL
⌘/Ctrl + Enter

UPDATE ... SET ... WHERE changes existing rows. Mark every shipped order as delivered:

SQL
⌘/Ctrl + Enter
SQL
⌘/Ctrl + Enter

DELETE ... WHERE removes rows. Always keep the WHERE — without it you delete the whole table:

SQL
⌘/Ctrl + Enter
SQL
⌘/Ctrl + Enter
P
PriyaFounder · Kettle & Co.now
Final one — we just onboarded Sara from India (id 6, signed up 2025-07-05). Add her, then show me every Indian customer’s name in alphabetical order so I can double-check she’s in. 🎉
🎯 Your mission INSERT Sara (id 6, India, signup 2025-07-05), then SELECT the name of all India customers, sorted A→Z.
SQL
⌘/Ctrl + Enter
You finished the course — every mission solved. 🏆 Reload the page to reset the data and practise again — or move on to the full Data Engineering course to see SQL running inside real pipelines.
Final Challenge

Boss Battle: The Quarterly Board Report

You've earned your stripes, analyst. 🎖️ The board meets Friday and I need the full revenue story — no training wheels this time. Three questions. Solve all three and the SQL Mastery badge is yours.

0 / 3 challenges solved
Challenge 1 / 3

Our biggest spenders

P
PriyaFounder · Kettle & Co.now
First up: who are our whales? 🐋 List every customer who has spent more than $100 in total — name and total spend, biggest first.
🎯 Your mission Return name and total spend for customers whose lifetime spend exceeds 100, highest first.
SQL
⌘/Ctrl + Enter
Challenge 2 / 3

The top spender in every country

P
PriyaFounder · Kettle & Co.now
Now segment it: for each country, who is our #1 spender? Give me country, name and their total — exactly one row per country. A window function earns its keep here. 🪟
🎯 Your mission Return country, name, and total spend for the single highest spender in each country (one row per country).
SQL
⌘/Ctrl + Enter
Challenge 3 / 3

The champion product

P
PriyaFounder · Kettle & Co.now
Last one. 🥇 What is our best-selling product by units sold? Product name and total units — just the single champion.
🎯 Your mission Return the name and total units sold of the one product with the most units ordered.
SQL
⌘/Ctrl + Enter

Want to go deeper into data?

SQL is the foundation. When you're ready for pipelines, Spark, and the modern data stack, the full Data Engineering course is free too.

Explore Data Engineering →
Found this course useful? Share it.
ShareXLinkedIn

Comments

0

Join the conversation. Sign in to leave a comment — we'd love to hear your thoughts.