Section 1 · Querying with Intent
Questions Before Queries
12 min3 min readSQL exercise
Lesson at a glance
Find your way around the Cartwheel database, turn a vague request into a precise question, and answer it with SELECT, computed columns, ORDER BY and LIMIT.
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Your first week at Cartwheel, a message lands from Rania Aziz, the CEO: “Which of our products are expensive but barely profitable? I want to look at pricing before the spring catalogue.” It sounds simple. It’s also a perfect example of the job: someone with a real decision to make asks a question in plain words, and you turn it into a query that answers exactly that question, no more and no less.
Cartwheel is an online store for home and outdoor goods, selling in eight countries. Its SQLite database holds two years of history, from January 2024 to December 2025. Every query in this course runs against it, in your browser.
Meet the database
Before writing the real query, look around. SQLite keeps a catalogue of its own tables in sqlite_schema (older code calls it sqlite_master, which still works):
SELECT name
FROM sqlite_schema
WHERE type = 'table';Nine tables. To see one table’s columns and types, ask for its column list:
SELECT name, type, "notnull"
FROM pragma_table_info('orders');Here’s how the tables connect. Arrows point from a table to the table it references; the label is the shared key.
The three highlighted tables are the spine of most questions: a customer places an order, and an order has one or more order items. Money lives on order_items, not on orders: line revenue is quantity * unit_price * (1 - discount).
From request to query
Rania’s request has two fuzzy words. “Expensive” can mean list price. “Barely profitable” needs a number: margin as a percentage of price, (unit_price - unit_cost) / unit_price, is the usual way to compare a $22 dripper with a $449 desk. Write the precise version down before you type: one row per product, showing price and margin percent, thinnest margins first.
SELECT
name,
unit_price,
unit_cost,
ROUND((unit_price - unit_cost) / unit_price * 100, 1) AS margin_pct
FROM products
ORDER BY margin_pct
LIMIT 5;Three habits are already at work. The computed column gets a readable name with AS. ORDER BY can sort by that alias. And LIMIT 5 comes last, cutting the sorted list.
The analyst’s loop
Every request in this course goes through the same loop, and it’s worth making it a habit now:
- Restate the question precisely, including what one row of the answer is.
- Explore the tables and columns you need.
- Query, starting small and building up.
- Sanity-check the result: row count, a total you know, an edge case.
- Answer in a sentence a non-analyst can act on.
For Rania, step 5 might read: “Our thinnest margins are around 40%, on the Burr Coffee Grinder and Compact Writing Desk; no product is listed below cost.” Short, specific, and it points to the next question.
Next you’ll add the most-used clause in analytics, WHERE, and learn where its logic trips people up.
Key takeaways
- An analyst’s query starts with a precise question, including what one row of the answer represents.
- Explore an unfamiliar database through its tables and columns before writing the real query.
- Computed columns with AS names make a result readable without changing the stored data.
- ORDER BY decides which rows LIMIT keeps, so a LIMIT without an ORDER BY returns an arbitrary sample.