Scenario: Brightlane's finance team needs both a backward-looking and forward-looking view of monthly revenue: each month should carry its own revenue plus the cumulative revenue earned to date and the revenue still to come.
Task: Write a query to return each order month, the revenue for that month, the cumulative revenue from the earliest month through that month, and the remaining revenue from that month through the latest month in the data.
Assumptions:
- An order month is identified by its first day.
- A month's
monthly_revenueis the combinedtotal_amountacrossordersplaced in that month. - A month's
cumulative_revenueis the combinedmonthly_revenuefrom the earliest month through that month inclusive. - A month's
remaining_revenueis the combinedmonthly_revenuefrom that month through the latest month in the data inclusive.
Output:
- One row per order month present in the data.
- Columns in this order:
order_month,monthly_revenue,cumulative_revenue,remaining_revenue. - Sorted by
order_monthascending.
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
DATE_TRUNC('month', ordered_at) AS order_month,
SUM(total_amount) AS monthly_revenue,
SUM(SUM(total_amount)) OVER (
ORDER BY
DATE_TRUNC('month', ordered_at) ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW
) AS cumulative_revenue,
SUM(SUM(total_amount)) OVER (
ORDER BY
DATE_TRUNC('month', ordered_at) ROWS BETWEEN CURRENT ROW
AND UNBOUNDED FOLLOWING
) AS remaining_revenue
FROM
orders
GROUP BY
DATE_TRUNC('month', ordered_at)
ORDER BY
order_month The shape
Two windows on the same partition look in opposite directions. The cumulative window uses ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW — every row from the start of the data through the current month. The remaining window uses ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING — the current month through the last month in the data. Both frames include the current row, so the two numbers double-count the current month's revenue by design, and their values overlap by exactly monthly_revenue at every row.
Clause by clause
SELECT DATE_TRUNC('month', ordered_at) AS order_month, SUM(total_amount) AS monthly_revenueproduces one row per order month with that month's revenue.SUM(SUM(total_amount)) OVER (ORDER BY DATE_TRUNC('month', ordered_at) ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_revenueruns the backward-looking accumulation from the earliest month through the current month.SUM(SUM(total_amount)) OVER (ORDER BY DATE_TRUNC('month', ordered_at) ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS remaining_revenueruns the forward-looking accumulation from the current month through the latest month.UNBOUNDED FOLLOWINGanchors the right side of the frame at the last row in the ordered partition.FROM orders GROUP BY DATE_TRUNC('month', ordered_at)aggregates the raw orders to one row per month so both windows operate on month-level rows.ORDER BY order_monthsorts the final output chronologically.
Why two windows and not total - cumulative + monthly
Computing remaining_revenue as total_revenue - cumulative_revenue + monthly_revenue would work numerically, but it requires materializing the total revenue across all months as a separate value and joining it back in. A second window function with CURRENT ROW AND UNBOUNDED FOLLOWING does the work in the same pass and reads as the direct symmetric of the cumulative window. The shape of the SQL matches the shape of the question: one frame looks backward, one looks forward, both anchored at the current row.
The trap
CURRENT ROW is included in both frames, so cumulative_revenue + remaining_revenue does not equal total revenue — it equals total revenue plus the current month's revenue (because the current month is counted twice, once as the end of the cumulative window and once as the start of the remaining window). This is the prompt's intended behavior: each row's remaining_revenue carries the current month's revenue forward as part of "from that month through the latest month," and each row's cumulative_revenue includes the current month as part of "through that month inclusive." Switching either frame to 1 PRECEDING or 1 FOLLOWING would remove the overlap but would also change what the column means. The double-count is structural, not a mistake.
You practiced pairing a backward-looking window (unbounded preceding) with a forward-looking window (unbounded following) on the same metric, so each row carries both the earned-to-date and remaining totals inline.