Brightlane's CRM team is building a customer order history view. Every order should be annotated with that customer's initial purchase amount for easy reference.
Write a query to return every order's ID, customer ID, order amount, and the amount of that same customer's very first order chronologically.
Assumptions:
- The
orderstable has one row per order with anid, acustomer_id, atotal_amount, and anordered_attimestamp. - A customer's first order is the order with the smallest
ordered_atfor thatcustomer_id. The same first-order amount appears on every row sharing acustomer_id. - The final result is sorted by
customer_idascending, then byordered_atascending.
Output:
- One row per order, with columns
id,customer_id,total_amount, andfirst_order_amount. Sorted bycustomer_id, thenordered_at.
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,
FIRST_VALUE(total_amount) OVER (
PARTITION BY
customer_id
ORDER BY
ordered_at
) AS first_order_amount
FROM
orders
ORDER BY
customer_id,
ordered_at The shape
FIRST_VALUE(total_amount) returns the value at position 1 of the ordered partition, and replicates that single value onto every row in the partition. Each order ends up sitting next to the dollar amount of that same customer's earliest order, with no join and no subquery.
Clause by clause
SELECT id, customer_id, total_amount, FIRST_VALUE(total_amount) OVER (PARTITION BY customer_id ORDER BY ordered_at) AS first_order_amountreturns each order's identifying columns plus the customer's first-order amount.PARTITION BY customer_idputs each customer's orders into their own window.ORDER BY ordered_atdefines the chronological sequence inside that window.FIRST_VALUEthen reaches into position 1 of that ordered sequence and pulls back thetotal_amountfrom there.FROM ordersreads every order. No filter; every order is included.ORDER BY customer_id, ordered_atsorts the printed result so each customer's history reads top to bottom in time order. This outer sort is separate from the window's sort; it controls display, not the lookup.
The trap
The first-order amount on the row that is the customer's first order equals that row's own total_amount, because position 1 of the partition is that very row. This is correct, not a bug. A reader who expects NULL or a fallback on the anchor row has imported the LAG mental model. FIRST_VALUE always has a value to return for a non-empty partition; there is no "no prior row" case to handle here.
You practiced FIRST_VALUE(column) OVER (PARTITION BY ... ORDER BY ...) — pull the value at position 1 of the ordered partition and replicate it onto every record in that partition.