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

Return each order's `id`, the `recorded_total` stored on the order, the `item_count` of its line items, and the `item_total` computed from those line items

Part of Analyst Debugging Patterns in SQL

The problem

Scenario: Brightlane's revenue analyst is investigating whether the total_amount stored on each order matches the combined value of its line items. The diagnostic places the recorded total alongside the line-item-derived total per order, so any discrepancies are visible inline.

Task: Write a query to return each order's id, the recorded_total stored on the order, the item_count of its line items, and the item_total computed from those line items.

Assumptions:

  • A line item's value is quantity multiplied by unit_price.
  • An order's item_count is the count of its line items; its item_total is the combined line-item value across all of its line items.
  • The result covers only orders with at least one line item on record.

Output:

  • One row per order with at least one line item.
  • Columns in this order: order_id, recorded_total, item_count, item_total.
  • Sorted by item_count 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
  item_counts AS (
    SELECT
      order_id,
      COUNT(*) AS item_count,
      SUM(quantity * unit_price) AS item_total
    FROM
      order_items
    GROUP BY
      order_id
  )
SELECT
  o.id AS order_id,
  o.total_amount AS recorded_total,
  ic.item_count,
  ic.item_total
FROM
  orders o
  JOIN item_counts ic ON ic.order_id = o.id
ORDER BY
  ic.item_count DESC

The shape

Aggregate the line items per order in a CTE first, then join that one-row-per-order summary back to orders so the recorded total and the line-item-derived total sit on the same row. The recorded value and the derived value are now in the same place, ready to be eyeballed for discrepancies without a second query.

Clause by clause

  • WITH item_counts AS (SELECT order_id, COUNT(*) AS item_count, SUM(quantity * unit_price) AS item_total FROM order_items GROUP BY order_id) collapses the many line items per order into one row per order. Both the count and the dollar total are computed in this CTE, so the next layer joins one-to-one rather than one-to-many.
  • SELECT o.id AS order_id, o.total_amount AS recorded_total, ic.item_count, ic.item_total returns four columns per order: the order id, the value stored on the order, and the two derived metrics from the CTE. Placing them in this order reads as "what we recorded vs. what the line items actually add up to."
  • FROM orders o JOIN item_counts ic ON ic.order_id = o.id pairs each order with its one summary row. The inner join drops orders with no line items, which is what the prompt asks for.
  • ORDER BY ic.item_count DESC puts the multi-item orders first. Those are the orders where a fanout-driven discrepancy would show up most loudly. The reference shows order 64 with three items, a recorded total of 1999, and an item_total of 2497.99 — a gap that an inline side-by-side display surfaces immediately.

The trap

Skipping the CTE and joining orders directly to order_items is where this problem turns into the inflated-aggregate class. SUM(oi.quantity * oi.unit_price) over that joined result is correct per group, but o.total_amount is the same value per order repeated once per line item — pull it through a sum without grouping by order and it multiplies. Pre-aggregating in the CTE collapses the many side to one row first, so when orders joins to item_counts the relationship is one-to-one and every parent column behaves the way the analyst expects.

You practiced placing recorded totals next to derived totals so the discrepancy per order is visible inline — a side-by-side diagnostic that surfaces which orders disagree without a second comparison query.

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.