Brightlane's operations team wants every order divided into three spend tiers within its order-status group for internal benchmarking.
Write a query to return every order's ID, status, total amount, and the order's spend tier within its status group. Sort the final result by status ascending, then total_amount ascending.
Assumptions:
- Within each status, orders are sorted by
total_amountascending and assigned to one of three tiers based on position. Tier1covers the lowest-value third of orders in that status; tier3covers the highest-value third. - Tier numbering restarts within each status.
- When the row count within a status does not divide evenly by
3, the earlier tiers each receive one extra record. - The final result is sorted by
statusascending, then bytotal_amountascending.
Output:
- One row per order, with columns
id,status,total_amount, andspend_tier. Sorted bystatus, thentotal_amount.
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,
status,
total_amount,
NTILE(3) OVER (
PARTITION BY
status
ORDER BY
total_amount
) AS spend_tier
FROM
orders
ORDER BY
status,
total_amount The shape
PARTITION BY status resets the bucketing inside each status group, so NTILE(3) cuts each status's orders into three equal-row spend tiers independently. Every order keeps its status, its dollar amount, and picks up a tier that ranges from 1 to 3 within its own status. The outer sort prints each status's orders together, ascending by amount.
Clause by clause
SELECT id, status, total_amount, NTILE(3) OVER (PARTITION BY status ORDER BY total_amount) AS spend_tierreturns the order's identifying columns and its within-status tier.PARTITION BY statusis what makes the tier numbering restart at 1 for each status;ORDER BY total_amountdefines the ascending sort the buckets are cut from inside each partition.FROM ordersreads every order across every status. NoWHEREfilter.ORDER BY status, total_amountis the outer sort. It prints all of one status's orders together, in ascending value, before moving to the next status.
Why this and not running NTILE without PARTITION BY
Without PARTITION BY status, NTILE(3) would cut the entire order set into three buckets and a delivered order with a small dollar amount could share tier 1 with a pending order of the same dollar amount. The operations team's benchmark is within-status: each status is its own population, and tier 1 means "the bottom third of orders in this particular status." The partition is what enforces that frame.
The trap
Tier numbers reset across statuses, so a tier-3 row in pending is not directly comparable to a tier-3 row in delivered. Each status has its own scale. Anyone reading the result has to read the tier in context with the status column; the tier number on its own is meaningless.
You practiced NTILE(3) OVER (PARTITION BY ... ORDER BY ...) — per-partition bucket distribution; tier numbering restarts in each group.