rangeBetween
Window frames by value instead of row count: the last 7 calendar days, not the last 7 rows.
On this page
Show code in
Every code block on the page follows this.
You will learn
- The difference between row frames and range frames
- How to compute a rolling 7-day total that respects missing days
- What the default frame is, and why it surprises people
- How to order a range frame by a date
rowsBetween(-6, 0) means the previous 6 rows; rangeBetween(-6, 0) means rows whose order value is within 6 units, so missing days are handled correctly.Frames: which rows the aggregate sees
A window function like sum(...).over(w) aggregates over a frame: a slice of the partition around the current row. You set it with rowsBetween(start, end) or rangeBetween(start, end), where negative means before, 0 means the current row, and Window.unboundedPreceding / unboundedFollowing mean the partition edges.
rowsBetween(-2, 0)
rangeBetween(-2, 0)
Step by step: a 7-day total
Store A has no sales rows on several days. We want, for each day, the total of the 7 calendar days ending that day.
| day | sales | rows(-6, 0) | range(-6, 0) |
|---|---|---|---|
| 03-01 | 10 | 10 | 10 |
| 03-02 | 20 | 30 | 30 |
| 03-03 | 30 | 60 | 60 |
| 03-07 | 40 | 100 | 100 |
| 03-10 | 50 | 150 ✗ | 90 ✓ |
On March 10 the last 7 calendar days are March 4 to 10, which contain only the 40 and the 50. The row frame grabs the previous 4 rows instead, reaching back to March 1, and over-counts.
Run the example
A range frame needs a numeric order key, so we turn the date into a day number first:
from pyspark.sql import functions as F from pyspark.sql.window import Window days = daily.withColumn("day_num", F.datediff("day", F.lit("1970-01-01"))) w = Window.partitionBy("store").orderBy("day_num") result = days.select( "day", "sales", F.sum("sales").over(w.rowsBetween(-6, 0)).alias("last_7_rows"), F.sum("sales").over(w.rangeBetween(-6, 0)).alias("last_7_days"), )
SELECT day, sales, SUM(sales) OVER (PARTITION BY store ORDER BY day ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS last_7_rows, SUM(sales) OVER (PARTITION BY store ORDER BY CAST(day AS DATE) RANGE BETWEEN INTERVAL 6 DAYS PRECEDING AND CURRENT ROW) AS last_7_days FROM daily
Switch to PySpark to edit and run this example in your browser.
In SQL you can order by a date and use an interval directly. In PySpark, convert to a number: days since epoch as above, or F.col("ts").cast("long") (seconds) for timestamps, with the frame in seconds, for example rangeBetween(-3600, 0) for the last hour.
The default frame trap
If a window has an orderBy but no frame, Spark uses RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: a running total. Because it is a range frame, rows that tie on the order value are all included together:
sum("x").over(Window.partitionBy("k").orderBy("day"))gives a running total in which every row of the same day shows the same value, the total up to and including that day.- Without
orderBy, the default frame is the whole partition, so the same expression gives the group total on every row. last("x").over(w)with the default frame returns the current row (or its last tie), not the partition's last value. UserowsBetween(unboundedPreceding, unboundedFollowing)for that.
Under the hood
Running frames (unbounded preceding to current row) are computed incrementally in one pass. Sliding frames are maintained by adding the row entering the frame and, where the function allows, removing the one leaving it. A frame like rangeBetween(-30, 0) on dense data still holds up to 30 days of rows per key in memory, so very wide frames on skewed keys can spill.
Common mistakes
Using rowsBetween for calendar windows
Ordering a range frame by a string
Forgetting the default frame
Key takeaways
- rowsBetween counts rows; rangeBetween uses the order value.
- Calendar windows (last N days) need rangeBetween.
- PySpark range frames need a numeric order key: days or seconds.
- orderBy without a frame means a running total; no orderBy means the whole partition.
Check yourself
3 questions1. Data has rows on days 1, 2 and 9. For day 9, what does sum over rangeBetween(-6, 0) on a day number include?
Show the answer
Only day 9. The range is days 3 to 9, and only day 9 has a row.
2. What frame does Window.partitionBy("k").orderBy("t") use by default for sum?
Show the answer
Unbounded preceding to current row, as a range. With an orderBy, the default is a running range frame up to the current row.
3. Why does rangeBetween on a string date column fail or misbehave?
Show the answer
Range frames need a numeric or date-time order key to subtract from. A range frame computes order value minus N, which needs a number, date or timestamp.
Practice it
Interview problems that use rangeBetween: write the PySpark, run it, and get graded on hidden tests.
Go deeper
Primary sources: WindowSpec.rangeBetween · Window functions (SQL)