N058-M1 Tier 5 · Expert · medium ecommerce · Brightlane

Return the name, total revenue, and total units sold for each product category that has generated more than `$500` in revenue across its line items

Part of Multi-CTE Query Architecture in SQL

The problem

Scenario: Brightlane's merchandising team is reviewing which product categories have crossed the $500 revenue threshold this period.

Task: Write a query to return the name, total revenue, and total units sold for each product category that has generated more than $500 in revenue across its line items.

Assumptions:

  • A line item's revenue is quantity multiplied by unit_price.
  • The result covers only categories whose total line-item revenue exceeds $500.

Output:

  • One row per qualifying category.
  • Columns in this order: category_name, revenue, units_sold.
  • Sorted by revenue descending.
Schema · ecommerce 5 tables
categories
id integer
name text
parent_id? integer
products
id integer
name text
category_id integer
price numeric
stock_qty integer
attributes? jsonb
order_items
id integer
order_id integer
product_id integer
quantity integer
unit_price numeric
customers
id integer
name text
email text
city? text
country text
created_at timestamptz
is_active boolean
orders
id integer
customer_id integer
ordered_at timestamptz
status text
total_amount numeric

Run previews · Check grades

Write a query, then run it to see results here.

Worked solution Try it yourself first
Solution query
WITH
  items_categorized AS (
    SELECT
      oi.quantity,
      oi.unit_price,
      c.name AS category_name
    FROM
      order_items oi
      JOIN products p ON oi.product_id = p.id
      JOIN categories c ON p.category_id = c.id
  ),
  category_revenue AS (
    SELECT
      category_name,
      SUM(quantity * unit_price) AS revenue,
      SUM(quantity) AS units_sold
    FROM
      items_categorized
    GROUP BY
      category_name
  ),
  top_categories AS (
    SELECT
      category_name,
      revenue,
      units_sold
    FROM
      category_revenue
    WHERE
      revenue > 500
  )
SELECT
  category_name,
  revenue,
  units_sold
FROM
  top_categories
ORDER BY
  revenue DESC

The shape

Three CTEs sequenced as combine, total, threshold. The first joins the three tables that have to come together for a categorised line item to exist, the second rolls those line items up to per-category revenue and units, and the third drops the categories that miss the $500 cut. Each transformation occupies a named layer instead of nesting inside a single statement.

Clause by clause

WITH items_categorized AS (
    SELECT oi.quantity, oi.unit_price, c.name AS category_name
    FROM order_items oi
    JOIN products p ON oi.product_id = p.id
    JOIN categories c ON p.category_id = c.id
)

Order lines reach their category through the products table, so two joins are needed: order_items to products for the category key, then products to categories for the readable name. The columns kept are just enough for the next layer to compute revenue and unit count.

category_revenue AS (
    SELECT category_name, SUM(quantity * unit_price) AS revenue, SUM(quantity) AS units_sold
    FROM items_categorized
    GROUP BY category_name
)

GROUP BY category_name produces one row per category. Both sums run over the already-categorised line items, so each category's totals reflect only its own products. Laptops lead with 21,287 in revenue across 13 units sold.

top_categories AS (
    SELECT category_name, revenue, units_sold
    FROM category_revenue
    WHERE revenue > 500
)

The threshold check runs against the rolled-up revenue value, which exists only after the previous layer. Eight categories clear $500; the rest drop out here.

  • SELECT category_name, revenue, units_sold FROM top_categories ORDER BY revenue DESC returns the qualifying categories with the highest revenue first.

Why three CTEs instead of one big query

The same answer can be written as a single SELECT with a HAVING clause and a multi-table join all in one expression. The result would match. The reason to split the work into three named layers is that each layer answers a different question: which rows belong to a category, how much each category sold, and which categories cleared the bar. When the report later needs a new threshold or a different join key, the change touches one CTE instead of an entangled monolithic statement.

You practiced sequencing three CTEs — combine, total, threshold — so each transformation occupies its own named layer instead of nesting inside a single statement.

How you actually get good at SQL

Reading explains SQL. Writing it, over and over with instant feedback, is what makes you fluent.

That's the whole SQLMaxx loop: 600+ real problems, instant AI feedback, mastery you can actually see, and spaced review that won't let you forget.

A stack of SQL practice problem cards, the top card showing an employees table.
615 problems · 66 concepts

Real problems. Not toy examples.

615 hand-built problems spanning all 66 concepts, from basic SELECTs to window functions, built on real schemas and real business questions, the kind you'll actually get asked on the job. Enough reps to make SQL automatic.

A retro computer showing a SQL query marked correct with a green checkmark.
Instant AI feedback

Write a query. Know if it's right in one second.

No copying an answer and hoping it clicked. The AI grader checks your real query against real data, catches exactly what's wrong, and explains the fix in plain English, like a senior analyst reading over your shoulder on every problem.

A circular mastery progress dial filling from blue to green, the SQLMaxx diamond at its center.
Mastery tracking

Stop guessing whether you actually know it.

SQLMaxx tracks every concept and shows you what you've mastered and what's still shaky. Your skills fill in one concept at a time, so 'I think I get joins' becomes something you can prove.

A SQL query editor circled by a blue return arrow with a clock, scheduled to come back for review.
Spaced review

Learn it once. Keep it for good.

Most of what you learn this week fades by next week. So when a concept comes due for review, SQLMaxx hands you a fresh problem to solve from a blank editor, not a flashcard to re-read. A research-backed spaced-repetition algorithm (FSRS) times each return for right before you'd forget, so your SQL is still there months later, when the interview or the job actually needs it.

Practice, feedback, mastery, review. That's the loop that turns reading into real skill.

Start free

No account, no credit card. Start solving in under a minute.