N042-H1 Tier 4 · Advanced · hard ecommerce · Brightlane

Return every order's ID, customer ID, order amount, and that same customer's chronologically previous order amount. Sort the final result by `customer_id` ascending, then by `total_amount` descending within each customer

Part of LAG and LEAD in SQL

The problem

Brightlane's account management team reviews each customer's order history with the largest orders surfaced first for quick scanning. The previous-order amount on each row must reflect chronological sequence — the order placed immediately before this one in time, not the row that happens to appear above it in the display.

Write a query to return every order's ID, customer ID, order amount, and that same customer's chronologically previous order amount. Sort the final result by customer_id ascending, then by total_amount descending within each customer.

Assumptions:

  • The previous-order amount is determined by ordered_at within each customer's orders — the order with the largest ordered_at strictly before the current row's ordered_at.
  • The final display sort has no effect on which order each row's previous-order amount comes from. The chronological lookup happens independently of the display order.
  • For a customer's earliest order chronologically — where no prior order is on record — the previous-amount value is missing.
  • The final result is sorted by customer_id ascending, then by total_amount descending.

Output:

  • One row per order, with columns id, customer_id, total_amount, and prev_order_amount. Sorted by customer_id, then total_amount 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
SELECT
  id,
  customer_id,
  total_amount,
  LAG(total_amount) OVER (
    PARTITION BY
      customer_id
    ORDER BY
      ordered_at
  ) AS prev_order_amount
FROM
  orders
ORDER BY
  customer_id,
  total_amount DESC

The shape

The window's ORDER BY ordered_at is what defines "previous order" for the LAG lookup; the query's outer ORDER BY customer_id, total_amount DESC only controls how the rows print. The two clauses run independently. The displayed order is dollar-sorted; the chronological lookup still happens against the time-sorted partition underneath.

Clause by clause

  • SELECT id, customer_id, total_amount, LAG(total_amount) OVER (PARTITION BY customer_id ORDER BY ordered_at) AS prev_order_amount returns each order's identifying columns and the chronologically-previous order amount. The window's ORDER BY ordered_at is the sort that determines which row is "previous"; this is fully resolved inside the window function, before the outer query touches the rows.
  • FROM orders reads every order.
  • ORDER BY customer_id, total_amount DESC sorts the printed result by customer, then by dollar amount descending. This sort runs after the window function has already produced the prev_order_amount values. It changes the display order, not the values.

Why the two ORDER BY clauses do not conflict

SQL evaluates window functions before the outer ORDER BY. By the time the outer sort runs, every row already carries its prev_order_amount, computed against the partition's chronological sequence. Reordering the rows after the fact does not re-run the lookup. Each row's prev_order_amount stays attached to that row no matter where it ends up in the printed output. The window's ORDER BY and the outer ORDER BY are different sorts at different stages of evaluation.

The trap

Removing or changing the window's ORDER BY clause would change the LAG lookup; removing the outer ORDER BY would not. The two clauses look identical in syntax and are not interchangeable. A reader who tries to "simplify" by collapsing them into one will either break the lookup (if they move it to the outer query) or scramble the display (if they remove the inner one). The two have to coexist because they answer two different questions: in what order does the lookup walk the rows, and in what order does the result print.

You practiced separating the window's ORDER BY (by ordered_at, defining 'previous') from the final query's ORDER BY (by total_amount descending, defining display order) — two independent sort instructions that don't bleed into each other.

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.