Skip to content

SQL Practice Exercises

Forty-two exercises across four guided paths. You write a real query, and it is checked by running it against a live sample database and comparing your rows against the expected answer — so any query that produces the right result is accepted, not just one exact wording.

This is the dedicated SQL workspace, with four learning paths and completion certificates. For Python, JavaScript and SQL in one searchable list, open All Coding Exercises.

Solved
0 / 42
Practice streak
0 days
Overall0%
Filter exercises by difficulty Show

Every exercise, at a glance

The full list of what you will be asked, in the order the paths run. Every question is checked by running your query against the sample database and comparing the rows it returns with the expected ones, so any query that produces the right result is accepted. Pick any one to start there.

Foundations 10 in this path · Beginner

Reading data out of a single table: choosing columns, filtering rows, sorting and limiting results.

  • The sales team wants to see everything we sell. Return every column of every row in the Products table.

    Tables: Products

  • For a price list, return only the name and unit_price of every product — nothing else.

    Tables: Products

  • Finance wants a list of products that cost more than 400. Return all columns for those products only.

    Tables: Products

  • Return the first_name, last_name and salary of every employee, with the highest earner first and the lowest earner last.

    Tables: Employees

  • Return the name and unit_price of just the three most expensive products, most expensive first.

    Tables: Products

  • Marketing wants a de-duplicated list of the countries our customers are based in. Return each country exactly once, in a single column named country.

    Tables: Customers

  • HR needs the first_name and last_name of employees who are in department 10 and are currently active (active = 1). Both conditions must hold.

    Tables: Employees

  • Return the company and tier of every customer whose tier is either Gold or Platinum.

    Tables: Customers

  • Return the company name of every customer whose company name starts with the letter A.

    Tables: Customers

  • Department heads have no manager, so their manager_id is empty. Return the first_name and last_name of every employee with no manager.

    Tables: Employees

Grouping & Joins 10 in this path · Intermediate

Summarising many rows into one, and combining tables that reference each other.

  • Return a single row with a single column named total_customers holding the number of rows in the Customers table.

    Tables: Customers

  • Return the average employee salary rounded to the nearest whole number, in one column named avg_salary.

    Tables: Employees

  • In one row, return the lowest unit_price as min_price and the highest as max_price from the Products table.

    Tables: Products

  • Cancelled and pending orders should not count as revenue. Return the sum of order_total for orders with status 'Shipped', in one column named shipped_revenue.

    Tables: Orders

  • Return each dept_id alongside the number of employees in it, with the count in a column named headcount.

    Tables: Employees

  • Return dept_id and headcount as before, but sort so the department with the most employees comes first.

    Tables: Employees

  • Return dept_id and headcount for departments that contain more than two employees.

    Tables: Employees

  • Employees only store a dept_id. Return each employee's first_name and last_name next to their dept_name by joining to the Departments table.

    Tables: Employees, Departments

  • Return every dept_name together with how many employees it has, named headcount. Departments with no employees at all must still appear, showing a count of 0.

    Tables: Departments, Employees

  • Return order_id, order_total, and a column named size_band that reads 'Large' when order_total is 8000 or more, 'Medium' when it is at least 3000 but under 8000, and 'Small' otherwise.

    Tables: Orders

Advanced Queries 8 in this path · Advanced

Queries inside queries, tables joined to themselves, and multi-step business questions.

  • Return the first_name, last_name and salary of every employee earning more than the average salary in their own department.

    Tables: Employees

  • Every customer has ordered something, but not every order ships. Return the company name of each customer who has had at least one order reach 'Shipped' status.

    Tables: Customers, Orders

  • Now the exact opposite: return the company name of each customer who has never had an order ship — everything they ordered is still pending or was cancelled.

    Tables: Customers, Orders

  • Return each employee's first_name alongside their manager's first_name in a column named manager_name. Employees with no manager should be left out.

    Tables: Employees

  • Build a single two-column list of everyone we deal with. Return each employee's first_name and each customer's contact under a shared column named person_name, plus a column named source containing 'Employee' or 'Customer' as appropriate.

    Tables: Employees, Customers

  • Return each product category alongside the total order_total it has generated from shipped orders only, in a column named category_revenue, highest revenue first.

    Tables: Orders, Products

  • Return the first_name and last_name of each employee together with how many orders they handled, in a column named orders_handled, counting shipped orders only, and listing the busiest employee first. Only include employees who handled at least one shipped order.

    Tables: Employees, Orders

  • Return company and the total spend as total_spent for every customer whose combined order_total across all their orders exceeds 10000, largest spender first.

    Tables: Customers, Orders

Expressions & Reports 14 in this path · Intermediate

CASE logic, text and date functions, CTEs, and the multi-step report queries real jobs actually ask for.

  • Marketing wants each product labelled. Return name, unit_price, and a column called price_label that says premium for products costing 500 or more and standard for everything else.

    Tables: Products

  • Support triages customers by tier. Return company, tier, and support_level: Platinum customers get 'Top priority', Gold customers get 'Standard support', and everyone else gets 'Self-serve'.

    Tables: Customers

  • Management wants the order pipeline on a single row: three columns named shipped, pending and cancelled, each counting the orders with that status.

    Tables: Orders

  • For each employee who has handled orders, return first_name, shipped_revenue (total order_total of their Shipped orders) and cancelled_revenue (total of their Cancelled orders), highest shipped_revenue first.

    Tables: Employees, Orders

  • HR needs mailing labels: a single column full_name (first name, a space, last name) plus email, for every employee, alphabetical by full_name.

    Tables: Employees

  • A colleague types 'an' into the customer search box and expects every company whose name contains those two letters, whether they are stored in upper or lower case - so 'Andes' counts as well as 'Meridian'. Return company.

    Tables: Customers

  • For the Engineering department (dept_id 10), return each employee's email and a column mailbox holding the email with '@example.com' removed.

    Tables: Employees

  • The catalogue layout breaks on long names. Return name and name_length for every product whose name is 18 characters or longer.

    Tables: Products

  • List every employee's first_name alongside reports_to - their manager's first name, or the text 'No manager' for employees at the top of the tree.

    Tables: Employees

  • How does order volume spread across the year? Return order_month (the month number from order_date) and orders (how many orders that month), latest month first.

    Tables: Orders

  • Define a CTE called big_orders that selects orders with order_total above 3000, then return order_id, company and order_total for those orders.

    Tables: Orders, Customers

  • Using a CTE that computes each department's average salary, return dept_name and avg_salary for the departments whose average exceeds 100000.

    Tables: Employees, Departments

  • Build a per-salesperson report using two CTEs: one counting each employee's Shipped orders, one totalling each employee's revenue across all orders. Return first_name, shipped_orders and revenue, highest revenue first.

    Tables: Employees, Orders

  • The quarterly report needs revenue by product category, ignoring cancelled orders: return category, orders (count) and revenue (sum of order_total), biggest revenue first.

    Tables: Orders, Products

Where your progress lives. There are no accounts here. Which exercises you have solved is stored only in this browser, using localStorage. It is never uploaded anywhere. That also means it does not follow you to another device or browser, and clearing your browsing data will erase it.

Frequently asked questions

Is this actually free?

Yes, all 42 exercises, the SQL Playground, and every blog tutorial are free with no paywall, no trial period, and no credit card. There are no user accounts to create.

Do I need to sign up or install anything?

No. Open this page and start writing queries. There is no database to install and no account to register — the SQL engine (SQLite, compiled to WebAssembly) runs inside your browser tab.

How is my answer checked — does it have to match exactly?

No. Your query and a reference solution both run against the same sample database, and the two result sets are compared row by row. Any query that produces the correct rows is accepted, even if it's phrased completely differently from the reference solution. Where a task names an output column, such as headcount, that name is part of the answer.

Does my SQL get sent to a server?

No. Every query — yours and the reference solution — runs locally in a Web Worker in your own browser. Nothing about what you write or submit is transmitted anywhere.

Is the Certificate of Completion an accredited qualification?

No. It's a self-issued record that you completed a learning path here, printable as a keepsake or portfolio item. It is not accredited and is not recognised by any university, employer or certifying body.