N044 Tier 4 · Advanced

Window Frames (ROWS, RANGE, GROUPS) in SQL

The window frame clause narrows the set of rows a window function operates on for each position in the partition. Where PARTITION BY divides the result set into independent windows and ORDER BY sequences the rows within each window, the frame clause defines a sliding boundary that moves with each row, determining exactly which rows contribute to that row's computation.

The window frame clause controls exactly which rows feed into a window function's calculation for each row. It's a sliding boundary that moves as the function processes the partition.

You've already used window functions that compute running totals: SUM(revenue) OVER (PARTITION BY region ORDER BY sale_date). What you may not have noticed is that a default frame is silently active. When you include ORDER BY inside OVER, the frame automatically covers from the start of the partition through the current row. That's why running totals accumulate correctly without any extra syntax.

Once you want a rolling window — the last 7 days, the last N rows, a specific date range — you need to declare the frame explicitly. ROWS and RANGE are the two modes you'll use in practice.

ROWS counts physical row positions. ROWS BETWEEN 6 PRECEDING AND CURRENT ROW means: the current row plus exactly the 6 rows before it in the sorted partition.

SUM(revenue) OVER (
    PARTITION BY region
    ORDER BY sale_date
    ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
)

If the data has one row per day, this is a 7-day rolling sum. But if there are gaps in the date sequence — a day with no data — ROWS still grabs exactly 6 prior rows regardless of their dates. You'd be summing across a longer calendar window than you intended.

RANGE operates on values, not positions. RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW includes all rows whose date falls within the 6 calendar days before the current row's date.

SUM(revenue) OVER (
    PARTITION BY region
    ORDER BY sale_date
    RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW
)

Gaps are handled correctly: missing days contribute nothing. The window always represents a true 7-day calendar range regardless of how many rows it contains.

With UNBOUNDED PRECEDING, ROWS and RANGE produce identical results on this data — you can confirm it:

The one thing that trips people up

ROWS and RANGE produce identical results when there are no gaps and one row per date. They diverge the moment gaps appear — and they diverge silently. Both queries run without error, but one returns the wrong values.

Use RANGE when gaps in the data matter and you want a true calendar window. Use ROWS when you need a fixed number of observations regardless of what dates they cover.

UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING

These boundary keywords cover the full partition. ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING covers every row for every calculation. This is the frame you need for LAST_VALUE and NTH_VALUE to behave as expected, and for any aggregate that should look at the entire group rather than a sliding window.

GROUPS is a third frame mode that counts peer groups (rows with equal ORDER BY values) rather than positions or value ranges. It's less common and covered when you encounter tied values in ranked windows.

Practice

9 Window Frames (ROWS, RANGE, GROUPS) practice problems

Write a query to return every order's ID, customer ID, order amount, and the combined `total_amount` across that customer's current order plus the two immediately preceding orders chronologically.

easy ecommerce

Write a query to return every session's ID, user ID, event count, and the average `event_count` across that user's current session plus the two immediately preceding sessions chronologically.

easy analytics

Write a query to return every order's ID, ordered-at timestamp, total amount, and the combined `total_amount` across that order plus the two orders placed immediately after it in the global chronological sequence.

easy ecommerce

Write a query to return every order's ID, ordered-at timestamp, total amount, and the combined `total_amount` across every order whose `ordered_at` falls within the 30 calendar days ending on and including that order's `ordered_at`.

medium ecommerce

Write a query to return every order's ID, customer ID, order amount, and the average `total_amount` across that customer's current order plus the six immediately preceding orders chronologically.

medium ecommerce

Write a query to return every delivered order's ID, customer ID, amount, and the average `total_amount` across that customer's current delivered order plus the two immediately preceding delivered orders chronologically.

medium ecommerce

Write a query to return every order's ID, customer ID, order amount, and the difference between that order's `total_amount` and the average across that customer's current order plus the two immediately preceding orders chronologically.

medium ecommerce

Write a query to return every order's ID, ordered-at timestamp, total amount, the combined `total_amount` across that order plus up to the two immediately preceding orders chronologically, and the count of orders included in that sum.

hard ecommerce

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.

hard ecommerce

These problems are part of the Window Frames (ROWS, RANGE, GROUPS) lesson in SQLMaxx, with instant grading and a worked solution on each.

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.