Brightlane's sales analyst wants a numbered sequence of each customer's orders sorted from most expensive to least expensive.
Write a query to return the ID, customer ID, and total amount of every order, plus a sequential number within that customer's orders, ordered by total_amount in descending order.
Assumptions:
- The
orderstable has one row per order with anid, acustomer_id, and atotal_amount. - Within each customer's orders, the position starts at
1for the highesttotal_amountand increments by1for each subsequent order. Numbering restarts for each customer. - If two orders for the same customer share the same
total_amount, they receive consecutive numbers in some order.
Output:
- One row per order, with columns
id,customer_id,total_amount, andorder_rank.
Schema · ecommerce 5 tables
Run previews · Check grades
Write a query, then run it to see results here.
Worked solution Try it yourself first
SELECT
id,
customer_id,
total_amount,
ROW_NUMBER() OVER (
PARTITION BY
customer_id
ORDER BY
total_amount DESC
) AS order_rank
FROM
orders The shape
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY total_amount DESC) restarts the numbering at 1 for each customer and counts down their orders from largest to smallest. The customer's biggest order is 1, second-biggest is 2, and the sequence is independent of how every other customer's orders fall.
Clause by clause
SELECT id, customer_id, total_amount, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY total_amount DESC) AS order_rankreturns each order's identifying columns and its rank within that customer.PARTITION BY customer_iddivides the orders into one bucket per customer;ORDER BY total_amount DESCsorts within each bucket;ROW_NUMBERthen numbers the rows of each bucket starting from1.FROM ordersreads every order in the system. No filter is applied; every customer's full history is in the result.
Why PARTITION BY and not a global ROW_NUMBER followed by a filter
A global ROW_NUMBER() OVER (ORDER BY total_amount DESC) would return the absolute rank across the entire orders table, which is a different question. The brief asks for per-customer ranks: each customer's biggest order should be 1, not the one customer who happens to have the largest dollar order overall. PARTITION BY is the construct that resets the window between customers; without it, the rank is computed against every order in the table at once.
The trap
Ties between two orders with the same total inside one customer's history are broken arbitrarily by ROW_NUMBER. If two orders for the same customer share a total_amount, one will get 1 and the other 2 even though the data says nothing about which is "first." Adding a secondary sort like ORDER BY total_amount DESC, id makes the tie-break deterministic. RANK or DENSE_RANK would carry the tie into the output instead of hiding it.
You practiced ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) — partition the numbering so each partition restarts at 1 independently.