SQL Exercises
Practise what you've learned. Type the missing piece into each highlighted gap and press Check. Further down are query challenges with live editors and model solutions — all using the demo shop database.
Fill in the blanks
Querying basics
Select every column from the products table.
SELECT FROM products;
* means all columns (lesson 03).
Name the calculated column "stock_value".
SELECT name, price * stock stock_value FROM products;
AS gives a column an alias (lesson 03).
Keep only customers in Portugal.
SELECT name FROM customers country = 'Portugal';
(lesson 04)
Find products that are not in the Books category.
SELECT name FROM products WHERE category 'Books';
<> is the standard "not equal" (lesson 04).
Sort products from most to least expensive.
SELECT name, price FROM products ORDER BY price ;
(lesson 06)
Return only the first three rows.
SELECT name FROM products ORDER BY price 3;
(lesson 07)
List each country only once.
SELECT country FROM customers;
(lesson 08)
Filtering
Match any of three statuses.
SELECT id FROM orders WHERE status ('pending', 'shipped', 'cancelled');
(lesson 09)
Find products priced from 20 to 40 inclusive.
SELECT name FROM products WHERE price 20 AND 40;
BETWEEN includes both ends (lesson 09).
Find product names starting with "W".
SELECT name FROM products WHERE name LIKE '';
% matches any characters (lesson 10).
Find customers with no city.
SELECT name FROM customers WHERE city ;
= NULL never matches (lesson 11).
Show 'Unknown' when the city is missing.
SELECT name, (city, 'Unknown') FROM customers;
(lesson 11)
Complete the CASE expression.
SELECT name, CASE WHEN stock = 0 THEN 'Sold out' ELSE 'In stock' AS availability FROM products;
Every CASE finishes with END (lesson 12).
Functions
Convert names to capital letters.
SELECT (name) FROM products;
(lesson 14)
Round prices to one decimal place.
SELECT name, round(price, ) FROM products;
(lesson 15)
Extract the year from each order date (SQLite).
SELECT strftime('', order_date) FROM orders;
%Y is the four-digit year (lesson 16).
Convert the text '42' into a number.
SELECT ('42' AS INTEGER) + 1;
(lesson 17)
Grouping
Count all rows in the orders table.
SELECT (*) FROM orders;
(lesson 18)
Average price per category.
SELECT category, AVG(price) FROM products category;
(lesson 19)
Keep only categories with more than two products.
SELECT category, COUNT(*) FROM products GROUP BY category COUNT(*) > 2;
WHERE can't filter on aggregates (lesson 20).
Joins and subqueries
Join each order to its customer.
SELECT o.id, c.name FROM orders AS o JOIN customers AS c c.id = o.customer_id;
(lesson 23)
Keep customers even if they have no orders.
SELECT c.name, o.id FROM customers AS c JOIN orders AS o ON o.customer_id = c.id;
(lesson 24)
Combine two lists, removing duplicates.
SELECT city FROM customers SELECT location FROM departments;
UNION ALL would keep duplicates (lesson 28).
Find products that have been ordered at least once.
SELECT name FROM products AS p WHERE (SELECT 1 FROM order_items AS oi WHERE oi.product_id = p.id);
(lesson 30)
Start a common table expression.
cheap AS (SELECT * FROM products WHERE price < 20) SELECT name FROM cheap;
(lesson 31)
Number rows within each category by price.
SELECT name, ROW_NUMBER() (PARTITION BY category ORDER BY price) AS pos FROM products;
(lesson 34)
Changing data and design
Add a new department.
INTO departments (name, location) VALUES ('Legal', 'Lisbon');
(lesson 35)
Change the status of order 12.
UPDATE orders status = 'shipped' WHERE id = 12;
(lesson 36)
Remove the items of order 5 (the cancelled order).
FROM order_items WHERE order_id = 5;
(lesson 37)
Undo everything since BEGIN.
BEGIN; DELETE FROM order_items; ;
(lesson 39)
Make the column required.
CREATE TABLE notes (id INTEGER PRIMARY KEY, body TEXT );
(lesson 42)
Link each note to a customer.
CREATE TABLE notes (id INTEGER PRIMARY KEY, customer_id INTEGER customers(id));
(lesson 43)
Speed up lookups of orders by date.
CREATE idx_orders_date ON orders (order_date);
(lesson 46)
Save a query as a reusable view.
CREATE toys AS SELECT * FROM products WHERE category = 'Toys';
(lesson 47)
Query challenges
1. Out-of-towners
List customers whose city is not Lisbon — including customers with no city — sorted by name.
Show a model solution
SELECT name, city FROM customers WHERE city <> 'Lisbon' OR city IS NULL ORDER BY name;
| name | city |
|---|---|
| Ben Carter | Manchester |
| Chloé Martin | Lyon |
| David Müller | Berlin |
| Elena García | Madrid |
| Farah Haddad | NULL |
| George Brown | London |
| Hana Novak | Prague |
| Isaac Levi | Porto |
2. Category report
For each category show the number of products, the cheapest and dearest price, and the total units in stock, most stock first.
Show a model solution
SELECT category, COUNT(*) AS products, MIN(price) AS cheapest, MAX(price) AS dearest, SUM(stock) AS units FROM products GROUP BY category ORDER BY units DESC;
| category | products | cheapest | dearest | units |
|---|---|---|---|---|
| Electronics | 4 | 14.99 | 89 | 255 |
| Home | 3 | 12.5 | 49 | 245 |
| Books | 3 | 24.9 | 45 | 73 |
| Toys | 2 | 18 | 39.9 | 62 |
3. Order receipts
For order 4, list each product name, quantity, unit price and line total, followed by the order total.
Show a model solution
SELECT p.name, oi.quantity, oi.unit_price, oi.quantity * oi.unit_price AS line_total FROM order_items AS oi JOIN products AS p ON p.id = oi.product_id WHERE oi.order_id = 4 UNION ALL SELECT 'TOTAL', SUM(quantity), NULL, SUM(quantity * unit_price) FROM order_items WHERE order_id = 4;
| name | quantity | unit_price | line_total |
|---|---|---|---|
| Ceramic Mug | 4 | 12.5 | 50 |
| Linen Throw | 1 | 49 | 49 |
| TOTAL | 5 | NULL | 99 |
4. Highest earner per department
Show the highest-paid employee in each department, with the department name.
Show a model solution
WITH ranked AS ( SELECT e.first_name, e.last_name, e.salary, d.name AS department, RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) AS r FROM employees AS e JOIN departments AS d ON d.id = e.department_id ) SELECT department, first_name, last_name, salary FROM ranked WHERE r = 1 ORDER BY salary DESC;
| department | first_name | last_name | salary |
|---|---|---|---|
| Engineering | Mei | Tanaka | 98000 |
| Sales | Rafael | Costa | 82000 |
| Marketing | Daniel | Kim | 71000 |
| Support | Ethan | Walsh | 60000 |
5. Customers who never ordered a book
List customers who have placed orders, but none containing a product in the Books category.
Show a model solution
SELECT c.name FROM customers AS c WHERE EXISTS (SELECT 1 FROM orders AS o WHERE o.customer_id = c.id) AND NOT EXISTS ( SELECT 1 FROM orders AS o JOIN order_items AS oi ON oi.order_id = o.id JOIN products AS p ON p.id = oi.product_id WHERE o.customer_id = c.id AND p.category = 'Books' ) ORDER BY c.name;
| name |
|---|
| Ben Carter |
| Chloé Martin |
| David Müller |
| George Brown |
| Hana Novak |
6. A safe price change
Inside a transaction, raise all Home prices by 10%, check the result with a query, then roll back and show that prices are unchanged.
Show a model solution
BEGIN; UPDATE products SET price = round(price * 1.1, 2) WHERE category = 'Home'; SELECT name, price FROM products WHERE category = 'Home'; ROLLBACK; SELECT name, price FROM products WHERE category = 'Home';
| name | price |
|---|---|
| Ceramic Mug | 13.75 |
| Desk Lamp | 37.4 |
| Linen Throw | 53.9 |
| name | price |
|---|---|
| Ceramic Mug | 12.5 |
| Desk Lamp | 34 |
| Linen Throw | 49 |