EduFlow
SQL for Analysts
0%

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.

  1. 1Meet the database
  2. 2From request to query
  3. 3The analyst’s loop

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):

SQL
SELECT name
FROM sqlite_schema
WHERE type = 'table';

Nine tables. To see one table’s columns and types, ask for its column list:

SQL
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.

Customers place orders; orders contain order_items; order_items reference products, which belong to categories. Returns reference order_items. Support tickets reference customers, orders and employees. Web sessions reference customers. web_sessions support_tickets employees customers orders order_items products categories returns customer_id order_id product_id
The Cartwheel schema: orders sit between customers and products

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.

SQL
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:

  1. Restate the question precisely, including what one row of the answer is.
  2. Explore the tables and columns you need.
  3. Query, starting small and building up.
  4. Sanity-check the result: row count, a total you know, an edge case.
  5. 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.

Further reading

Check your understanding

3 questions

Question 1 of 3

Rania asks for “our best products”. What should you do before writing any SQL?

Question 2 of 3

What does this query return?

SQL
SELECT name, unit_price
FROM products
LIMIT 3;
Question 3 of 3

You write unit_price - unit_cost AS margin in the SELECT list. What happens to the products table?

Practice

SQL exercise

Find the fattest margins

Rania liked the thin-margin list and now wants the opposite: the 5 products with the highest margin percent, so marketing can feature them.

Return four columns in this order: name, unit_price, margin (unit_price - unit_cost, rounded to 2 decimals) and margin_pct (margin as a percentage of unit_price, rounded to 1 decimal). Sort by margin_pct, highest first. Order matters.

The starter sorts by margin in dollars, which favours expensive products. Fix it.

Tables
customers
(customer_id, first_name, last_name, email, country, city, signup_date, segment, referred_by)
orders
(order_id, customer_id, order_date, status, channel, coupon_code, shipping_fee)
order_items
(order_item_id, order_id, product_id, quantity, unit_price, discount)
products
(product_id, name, category_id, unit_price, unit_cost, launched_on, is_active)
categories
(category_id, name, parent_id)
returns
(return_id, order_item_id, return_date, reason, refund_amount)
employees
(employee_id, first_name, last_name, title, department, manager_id, hire_date, salary)
support_tickets
(ticket_id, customer_id, order_id, opened_at, closed_at, priority, channel, satisfaction, agent_id)
web_sessions
(session_id, customer_id, started_at, source, device, pages_viewed, added_to_cart, purchased)
-- Highest-margin products, by margin percent
SELECT
  name,
  unit_price,
  ROUND(unit_price - unit_cost, 2) AS margin
FROM products
ORDER BY margin DESC
LIMIT 5;Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.