Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Answering a manager's question with SQL

After this lesson: I can answer a manager's question with a query.

45 min lesson9 commandsMySQL and phpMyAdmin lab with a sample shop database to practise SQL

Three questions before lunch

You have a Saturday job at a small online gift shop. On Monday the manager sends three questions. What is nearly sold out? How many orders are still waiting? How much did we really take?

The answers are all in the shop's database. One short query each finds them in a second.

By the end of this lesson

Note

I can answer a manager's question with a query.

Key words

WordWhat it meansExample
Querya question you ask a database in SQL
Reportan answer set out for somebody to act on
Conditiona test each row passes or failsstock < 5
Aggregatea function that turns many rows into one valueSUM
Groupthe rows that share one valueGROUP BY status
Aliasa new name for a column in the answerAS orders

The cells students fill in are left blank in this preview.

Sign in to the shop

Start the lab if it is not running. If your terminal is new, press these two Runs again. They let the terminal trust the lab's server and make the short name sql.

Trust the lab's server

Command
printf '[client]\nssl-verify-server-cert=0\n' > ~/.my.cnf

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Make a short name for signing in

Command
alias sql='mariadb -h db -u student -pstudent shop -t -e'

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Turn the question into a condition

"What is nearly sold out?" is too vague for a database. Decide what "nearly" means first. Say fewer than 5 left.

Then the query writes itself. WHERE stock < 5 keeps those rows. ORDER BY stock puts the emptiest first. LIMIT 3 keeps only the first three rows.

Which products are nearly sold out?

Command
sql "SELECT name, stock FROM products WHERE stock < 5 ORDER BY stock, name"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

The three dearest products

Command
sql "SELECT name, price FROM products ORDER BY price DESC LIMIT 3"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

An aggregate turns rows into one number

"How many orders are waiting?" wants one number, not a list. COUNT(*) counts the rows that pass the condition.

GROUP BY goes one step further. It splits the rows into groups that share a value, then counts each group. One query answers "how many of each?"

How many orders are waiting?

Command
sql "SELECT COUNT(*) AS waiting FROM orders WHERE status = 'pending'"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

How many orders of each status?

Command
sql "SELECT status, COUNT(*) AS orders FROM orders GROUP BY status ORDER BY orders DESC"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Where do the customers live?

Command
sql "SELECT country, COUNT(*) AS customers FROM customers GROUP BY country ORDER BY customers DESC, country"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Try it: a café till

Code: sql
-- COUNT(*) counts the rows: how many times each item was rung up.
-- Change COUNT(*) to SUM(qty) and press Run: now it counts the items sold.
SELECT item, COUNT(*) AS sales
FROM sales
GROUP BY item
ORDER BY sales DESC, item;

Check the answer fits the question

A query can run with no error and still give the wrong answer. "How much did we take?" means money that really came in.

A cancelled order was never paid. So the first Run below adds up every order line, and the second leaves the cancelled orders out.

Every order line added up

Command
sql "SELECT SUM(quantity * unit_price) AS takings FROM order_items"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

The takings without cancelled orders

Command
sql "SELECT SUM(order_items.quantity * order_items.unit_price) AS takings FROM order_items JOIN orders ON orders.id = order_items.order_id WHERE orders.status <> 'cancelled'"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

A common mistake

Warning

Many people think a query that runs is a query that is right. The database only does what you wrote. Before you send a report, say in one line which rows it counts and which it leaves out, such as "not counting cancelled orders".

Going further: takings by year

YEAR(ordered_at) turns a date and time into just the year. Group by it to compare the shop's years.

Write a query in the lab that shows the takings for each year, with the cancelled orders left out. Which year was best, and why might 2023 look small?

About this lab and its source: MySQL and phpMyAdmin lab with a sample shop database to practise SQL

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.