Rolling period calculation in BigQuery

Senior Data Engineer • Contractor / Freelancer • GCP & AWS Certified
Search for a command to run...

Senior Data Engineer • Contractor / Freelancer • GCP & AWS Certified
No comments yet. Be the first to comment.
A focused guide to window functions in BigQuery — cumulative sums, rolling averages, ranking functions, named windows, RANGE frames, and more.
Here's something I found out about the ORDER BY clause in BigQuery SQL the other day. Check out the NULLS FIRST / NULLS LAST clauses. What do they do? They control how to treat NULL values when sorting. While these are entirely optional, they're actu...
Here's a useful Dataform concept: pre_operations and post_operations. As the name implies, these represent a set of actions that run before and after the main operation (table, view, or SQL operations

BigQuery has always been a SQL engine for tabular data. Object tables add an interesting twist to that. Instead of rows containing values, an object table gives you one row per file — pointing at da

Query your data lake with warehouse-grade security and performance — without moving a single file.

Ever run a heavy BigQuery SQL query, processed gigabytes of data — and then accidentally closed the tab or forgot to save the results? 😬 Don't re-run it. Your results are still there. BigQuery automa

You can use query parameters in BigQuery hashtag#SQL (now in the console as well!) — but how are they different from variables, and when should you use each? Both parameters and variables act as place

How to compute a rolling period calculation in BigQuery?
In one of my previous posts, I've showcased using RANGE inside window function declarations in the context of computing a cumulative sum.
Today we're going to look at another example often found in the wild - computing a rolling period calculation. Let's look at an example.
We have customer order information and would like to compute a per customer rolling sum of the previous 60 days' worth of purchases.
How this would look in terms of SQL?
SUM(order_total) OVER (PARTITION BY customer_id ORDER BY UNIX_DATE(order_date) RANGE BETWEEN 59 PRECEDING AND CURRENT ROW) AS rolling_60_days_sum
Let's explain it:
We will start by taking a SUM of order_total with a window declaration
and partitioning by customer_id.
Next is ordering by our order_date, but since RANGE only accepts a single integer field, we'll need to transform it using UNIX_DATE. This will transform '2021-01-01' into 18628.
We can now use RANGE, setting the range between the 59 previous days and the current row. This way, if we have any gaps or duplicates in our order data (which is very likely), the calculation would still work, as opposed to the approach of using ROWS.
See below an illustration of how it all works.
SELECT
customer_id,
order_id,
order_total,
order_date,
SUM(order_total) OVER (PARTITION BY customer_id
ORDER BY UNIX_DATE(order_date)
RANGE BETWEEN 59 PRECEDING AND CURRENT ROW)
AS rolling_60_days_sum
FROM input_data
+-------------+----------+-------------+------------+---------------------+
| customer_id | order_id | order_total | order_date | rolling_60_days_sum |
+-------------+----------+-------------+------------+---------------------+
| Customer-1 | 10001 | 100 | 2021-01-01 | 100 |
| Customer-1 | 10003 | 75 | 2021-02-15 | 175 |
| Customer-1 | 10005 | 90 | 2021-03-12 | 165 |
| Customer-1 | 10001 | 100 | 2021-04-21 | 190 |
| Customer-1 | 10003 | 75 | 2021-05-12 | 175 |
| Customer-1 | 10005 | 90 | 2021-06-23 | 165 |
| Customer-2 | 10002 | 80 | 2021-01-01 | 80 |
| Customer-2 | 10004 | 120 | 2021-02-04 | 200 |
| Customer-2 | 10006 | 50 | 2021-03-05 | 170 |
| Customer-2 | 10002 | 80 | 2021-04-11 | 130 |
| Customer-2 | 10004 | 120 | 2021-05-12 | 200 |
| Customer-2 | 10006 | 50 | 2021-06-30 | 170 |
+-------------+----------+-------------+------------+---------------------+
Found it useful? Subscribe to my Analytics newsletter at notjustsql.com.
Enjoyed this? Here are some related articles you might find useful: