N044-H2 Tier 4 · Advanced · hard ecommerce · Brightlane

Return every order's ID, ordered-at timestamp, total amount, the position-based rolling-3 sum, and the 30-day calendar rolling sum

Part of Window Frames (ROWS, RANGE, GROUPS) in SQL

The problem

Brightlane's finance team wants to compare two rolling revenue metrics side by side — one based on a fixed number of orders (the current order plus the two immediately preceding orders by position) and one based on a 30-day calendar window (every order whose date falls in the trailing 30-calendar-day range).

Write a query to return every order's ID, ordered-at timestamp, total amount, the position-based rolling-3 sum, and the 30-day calendar rolling sum.

Assumptions:

  • The position-based rolling-3 sum at each row covers that order plus the two orders with the largest ordered_at strictly before it. Three records always, except for the first two rows in the stream where the window is partial.
  • The 30-day calendar rolling sum at each row covers every order whose ordered_at is within 29 calendar days before the current row's ordered_at, plus the current order. The number of records varies with how many fall into the calendar range — gaps in the date sequence reduce the count; clustered orders increase it.
  • On rows where the position-based and calendar-based windows happen to contain different sets of orders, the two sums will differ — that divergence is the point of the side-by-side comparison.
  • The final result is sorted by ordered_at ascending.

Output:

  • One row per order, with columns id, ordered_at, total_amount, rolling_3_orders, and rolling_30d. Sorted by ordered_at.
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,
  ordered_at,
  total_amount,
  SUM(total_amount) OVER (
    ORDER BY
      ordered_at ROWS BETWEEN 2 PRECEDING
      AND CURRENT ROW
  ) AS rolling_3_orders,
  SUM(total_amount) OVER (
    ORDER BY
      ordered_at RANGE BETWEEN INTERVAL '29 days' PRECEDING
      AND CURRENT ROW
  ) AS rolling_30d
FROM
  orders
ORDER BY
  ordered_at

The shape

Two windowed sums over the same ORDER BY produce a side-by-side comparison of position-based and calendar-based rolling totals. ROWS BETWEEN 2 PRECEDING AND CURRENT ROW counts three physical rows. RANGE BETWEEN INTERVAL '29 days' PRECEDING AND CURRENT ROW counts every row inside a 30-day calendar span. Both run in the same query; the divergence between them is the analytical point.

Clause by clause

  • SELECT id, ordered_at, total_amount, SUM(total_amount) OVER (ORDER BY ordered_at ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS rolling_3_orders, SUM(total_amount) OVER (ORDER BY ordered_at RANGE BETWEEN INTERVAL '29 days' PRECEDING AND CURRENT ROW) AS rolling_30d returns the order identifiers and the two windowed sums. The first OVER uses a ROWS frame: exactly three row positions. The second uses a RANGE frame: every row whose ordered_at is within a 29-day interval of the current row. Both share the global ordering on ordered_at.
  • FROM orders reads every order.
  • ORDER BY ordered_at sorts the result chronologically so the two trends read in parallel.

Why both frames and not one

Each mode answers a different question. ROWS answers "what were the last three orders' worth of revenue?" RANGE answers "what was revenue inside the last 30 calendar days?" On a steady one-order-per-day stream they would agree. The moment gaps or clusters appear, they diverge: a slow week shrinks the calendar window's row count while the position window keeps grabbing three rows from further back; a busy day swells the calendar window past three rows while the position window stays at three. The point of running both is to make that divergence quantitative.

The trap

Both queries succeed silently with whatever data exists. Neither raises an error when gaps appear. The 30-day calendar sum on a quiet stretch can drop to a single row's value, and a reader skimming the column would not see anything wrong until they noticed the rolling-3 column reporting a much larger number for the same row. The bug class is "the query runs, the numbers look plausible, the meaning is different from what the reader assumed." The fix is to read the frame clause, not the column name. rolling_30d and rolling_3_orders mean what their frames say they mean, not what an analyst's intuition reaches for first.

You practiced ROWS vs RANGE over the same ORDER BYROWS counts records (always exactly N when the window is full); RANGE counts a value range (record count varies with how data clusters).

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.