Top Products Last 3 Months
Find the products with the highest revenue in the last 3 months of the data, counting back from the most recent order date.
The challenge runs on the SQLabHub practice database (SQLite) and uses the orders and order_items tables. You can solve it in the browser, with nothing to install.
The task
Find the products with the highest revenue in the last 3 months of the data, counting back from the most recent order date.
A correct answer returns 37 rows with 2 columns: product_id, revenue.
Tables used in this challenge
orders 200 rows
id INTEGER (PK) · user_id INTEGER · total REAL · discount REAL · item_count INTEGER · status TEXT · order_date TEXT · shipped_date TEXT · shipping_city TEXT · shipping_country TEXT
| id | user_id | total | discount | item_count | status | order_date | shipped_date | shipping_city | shipping_country |
|---|---|---|---|---|---|---|---|---|---|
order_items 496 rows
id INTEGER (PK) · order_id INTEGER · product_id INTEGER · quantity INTEGER · unit_price REAL · subtotal REAL
| id | order_id | product_id | quantity | unit_price | subtotal |
|---|---|---|---|---|---|
The first rows of each table are shown. See how all the tables relate on the database schema page.
Expected output
| product_id | revenue |
|---|---|
First 5 of 37 rows. This is the output of the model solution on the practice database.
What you practise
- Filtering rows with a WHERE condition
- Summarising many rows into one value with COUNT, SUM, AVG, MIN or MAX
- Grouping rows with GROUP BY to get one result row per group
- Combining rows from two tables on a matching key with an inner join
- Using the result of one query inside another
- Working with dates in SQLite: strftime, julianday and DATE
- Sorting the result with ORDER BY
Hint and approach
Try the challenge first. Open these only when you need them.
Show the hint
Anchor the date filter on MAX(order_date), then GROUP BY product
Show the step-by-step approach
How the model solution is built, in the order the database evaluates it:
- Read from the
order_itemstable. - Join
ordersonorder_items.order_id=orders.id. - Filter in
WHERE: keep the rows whereorders.order_dateis at leastDATE((SELECT MAX(order_date) FROM orders), '-3 month'). - The subquery runs once and returns a single value. It works like this:
- Read from the
orderstable. - Aggregate over all the rows that are left: compute
MAX(order_date). With no GROUP BY, the result is a single row.
- Read from the
- Group the rows by
order_items.product_id, so the result has one row for each value. - For each group, compute
SUM(order_items.subtotal). - Sort the result by
revenuedescending.
Model solution
Checking your session…
What to watch for
- INNER JOIN. An inner join drops rows that have no partner on the other side, and it repeats a row once for every match, which inflates SUM and COUNT.
- GROUP BY. Every selected column must either be in GROUP BY or sit inside an aggregate. SQLite accepts other columns and returns an arbitrary value for them; most databases raise an error.
- Date functions. These date functions are SQLite syntax. MySQL uses DATE_FORMAT and DATEDIFF, PostgreSQL uses TO_CHAR, EXTRACT and date subtraction.
Related SQL challenges
- Count Orders in Each Customer's First 7 DaysSimilar levelHard
- Find Products with No Sales in the Last Month of DataSimilar levelHard
- Count Orders per User in the Latest Year of DataEasierMedium
- Find Orders from the Last 30 Days of DataEasierMedium
- Calculate Monthly Revenue with strftimeEasierMedium
Keep learning
- Lesson: INNER JOIN
- Lesson: GROUP BY
- Lesson: Date Functions
- Lesson: SQL Subqueries
- All SQL practice challenges, by difficulty and topic
- SQL interview questions with answers
- SQL Playground: run any query on the same database