SQL foundations: ask better questions
Learn SQL by asking a small sales database clear questions with SELECT, JOIN, GROUP BY, and hands-on queries explained in plain language.
Learn it here, one small step at a time.
4 guided lessons · About 2h 30m including practice · No subscription
- Read and filter rows with SELECT and WHERE
- Connect related tables with JOIN
- Summarize results with GROUP BY and HAVING
Ask for rows with SELECT
SQL is a way to ask a database a precise question. A table stores rows (records) and columns (fields). SELECT chooses what to show, FROM chooses the table, and WHERE keeps only rows that match a condition. SQL keywords are often written in capitals for readability; names and text values still need to match the database.
Try each part in order: choose the table, choose a few useful columns, then narrow the result. Finish a statement with a semicolon.
| sale_id | region | item | amount |
|---|---|---|---|
| 1 | North | Notebook | 12 |
| 2 | South | Pen | 10 |
| 3 | North | Folder | 15 |
Write a query that shows only `region` and `amount` for rows where amount is at least 12. Then change the condition to `region = 'North'`.
Reveal a suggested answer
It keeps only rows whose region value is North; it does not choose which columns appear.
Optional further reading
Core lesson and practice are on this page. For more detail, visit the official free reference ↗.
Match records with JOIN
Related information is often stored in separate tables. A customer table can have one row per customer, while an orders table has one row per order. JOIN matches the tables using a shared key such as customer_id. Use a table alias to make long queries easier to read.
INNER JOIN returns matches from both sides. LEFT JOIN keeps every row on the left and fills missing right-side values with NULL. Check how many rows you get; a join can create extra rows when a key is not unique.
| Table | Key | One row represents |
|---|---|---|
| customers | customer_id | One customer |
| orders | customer_id | One order |
| joined result | customer_id | One matching customer-order pair |
Join `customers` to `orders` on `customer_id`. Use a LEFT JOIN and look for rows where `orders.order_id IS NULL` to find customers without orders.
Reveal a suggested answer
A customer may have several orders, so there is one result row for each matching customer-order pair.
Optional further reading
Core lesson and practice are on this page. For more detail, visit the official free reference ↗.
Count and group results
An aggregate turns many rows into a summary: COUNT(*) counts rows and SUM(amount) adds amounts. GROUP BY region asks for one summary per region. Use WHERE to filter individual rows before grouping; use HAVING to filter completed groups afterward.
A summary is only meaningful when the grouping level is clear. Grouping sales by Region answers a different question from grouping by Region and Item.
| Clause | Simple meaning | Example |
|---|---|---|
| WHERE | Choose rows first | WHERE amount > 10 |
| GROUP BY | Make groups | GROUP BY region |
| HAVING | Choose groups | HAVING SUM(amount) > 20 |
| ORDER BY | Sort the result | ORDER BY total DESC |
Calculate total sales and number of sales per region. Keep only regions with a total above 20, and sort the largest total first.
Reveal a suggested answer
HAVING filters groups; WHERE filters the original rows before grouping.
Optional further reading
Core lesson and practice are on this page. For more detail, visit the official free reference ↗.
Put the query together
Here is a complete question: “Which regions sold more than $20?” Translate it into steps: select the region, add amounts, group by region, keep groups above 20, then sort. Read the query from the data source through the filters instead of memorizing a magic line.
SELECT region, SUM(amount) AS total_sales
FROM sales
GROUP BY region
HAVING SUM(amount) > 20
ORDER BY total_sales DESC;
Test a query on a few known rows and check its total against the source. A clear column alias, such as total_sales, makes the result easier for another person to understand.
| Check | Ask yourself |
|---|---|
| Grain | What does one result row represent? |
| Join key | Does it match the intended records? |
| Totals | Can I reconcile them with the source? |
Change the query to include a count of sales. Explain in one sentence whether the count means orders or customers.
Reveal a suggested answer
A clear alias labels the result for people and makes it easy to refer to when sorting or using the query elsewhere.
Optional further reading
Core lesson and practice are on this page. For more detail, visit the official free reference ↗.
Recommended next steps
These paths build on ideas from this course. You can start any of them whenever you are ready.
Your final knowledge check.
Score 80% or more to pass. Sign in to save your best result.
