SQL basics: reading data
Practice: questions about the shop
The idea in 30 seconds
Eight questions about the shop, each answerable with one SELECT. Write each query yourself before opening the answer. Along the way you will use comparison and range filters, pattern matching, lists of values, DISTINCT and CASE.
Helps to know first: Install PostgreSQL and learn the environment
On this page
How to use this page
Connect with psql shop. For each question, write and run your query before opening the answer. If your result matches ours row for row, you are right, even if your query looks different. Many correct queries exist for each question.
New tools used here, in one line each:
| Tool | Example | Meaning |
|---|---|---|
BETWEEN | price BETWEEN 1000 AND 2000 | Between the two values, including both ends |
IN | status IN ('paid', 'shipped') | Equal to any value in the list |
LIKE / ILIKE | name ILIKE '%usb%' | Matches a pattern; % means any characters; ILIKE ignores case |
DISTINCT | SELECT DISTINCT category | Remove duplicate rows from the result |
CASE | CASE WHEN price < 2000 THEN 'budget' ELSE 'other' END | Choose a value by condition |
The questions
List the products under 2,000, cheapest first, with their prices.
Show answer
SELECT name, price FROM products WHERE price < 2000 ORDER BY price;name | price -----------------+--------- Cable organiser | 499.00 Wireless mouse | 1200.00 Laptop stand | 1899.00Which customers joined between 1 April and 30 June 2025, in the order they joined?
Show answer
SELECT name, joined_on FROM customers WHERE joined_on BETWEEN '2025-04-01' AND '2025-06-30' ORDER BY joined_on;name | joined_on ---------------+------------ Elena Petrova | 2025-04-15 Farhan Ali | 2025-05-01 Grace Kim | 2025-06-22What product categories does the shop sell? Each category once, alphabetically.
Show answer
SELECT DISTINCT category FROM products ORDER BY category;Six rows: Accessories, Audio, Displays, Furniture, Storage, Video.
Show the five most recent orders that are paid or shipped.
Show answer
SELECT id, customer_id, status, ordered_at FROM orders WHERE status IN ('paid', 'shipped') ORDER BY ordered_at DESC LIMIT 5;id | customer_id | status | ordered_at -----+-------------+---------+------------------------ 112 | 6 | shipped | 2025-09-09 05:00:00+00 110 | 3 | shipped | 2025-08-02 05:25:00+00 109 | 7 | shipped | 2025-07-19 11:10:00+00 108 | 1 | paid | 2025-07-03 06:00:00+00 107 | 6 | paid | 2025-06-14 11:45:00+00Times are shown in UTC; the practice database sets that so everyone sees the same values.
Find the products whose name mentions USB or SSD, in any capitalization.
Show answer
SELECT name FROM products WHERE name ILIKE '%usb%' OR name ILIKE '%ssd%';Two rows: USB-C hub and Portable SSD 1TB.
Label every product as budget (under 2,000), mid-range (under 10,000) or premium, cheapest first.
Show answer
SELECT name, price, CASE WHEN price < 2000 THEN 'budget' WHEN price < 10000 THEN 'mid-range' ELSE 'premium' END AS band FROM products ORDER BY price;Three budget, five mid-range, three premium.
CASEtakes the firstWHENthat is true, so a price of 1,500 is “budget” even though it is also under 10,000.Which customers do we have no city for?
Show answer
SELECT name FROM customers WHERE city IS NULL;Elena Petrova and Hugo Martin.
WHERE city = NULLreturns nothing, because the comparison is unknown for every row.List every customer who does not live in Pune, including those whose city we do not know.
Show answer
SELECT name, city FROM customers WHERE city IS DISTINCT FROM 'Pune' ORDER BY name;Six rows.
WHERE city <> 'Pune'would return only four, missing Elena and Hugo.
If you got stuck
Two mistakes cause most wrong answers on this page:
- Forgetting
ORDER BY, so the rows are right but the order is not, orLIMITpicks the wrong ones. - Comparing with NULL using
=or<>, which silently drops the rows whose value is unknown.
Finished reading?
Saved in this browser only.
Sources
Where each claim on this page comes from.
- 1.PostgreSQL documentation: SELECTPrimary· SELECT List, DISTINCT Clause
SELECT DISTINCT removes duplicate rows from the result.
- 2.PostgreSQL documentation: 9.2 Comparison Functions and OperatorsPrimary· §9.2 Comparison Functions and Operators
BETWEEN includes both of its end values.
- 3.PostgreSQL documentation: 9.18 Conditional ExpressionsPrimary· §9.18.1 CASE
CASE returns the result of the first WHEN condition that is true, or the ELSE value.
- 4.PostgreSQL documentation: 9.7 Pattern MatchingPrimary· §9.7.1 LIKE
LIKE matches patterns where % stands for any sequence of characters; ILIKE does the same ignoring case.
Go further
What to read next, and why it is worth the time.
- 1.PostgreSQL documentation: 9.7 Pattern MatchingPrimary· §9.7.1 LIKE
Wildcards, escaping, and the case-insensitive ILIKE, in two screens.
Applies to PostgreSQL 18. Draft: not yet fact-checked end to end.