Back to Blog

How to Configure Incremental Refresh in Power BI Without Breaking Your Model

July 30, 20268 min readMichael Ridland

If you have ever sat watching a Power BI dataset refresh for forty minutes and wondered why it needs to reload three years of sales data every single morning when only yesterday changed, this post is for you. That daily full reload is the default behaviour, and for a small model nobody notices. For a model with tens of millions of rows it becomes the thing that makes the whole reporting platform feel slow, unreliable, and prone to timing out right when the finance team needs the numbers.

Incremental refresh is the fix, and it is one of those features that pays for itself the first week you turn it on. The idea is simple. Instead of reloading everything, Power BI only refreshes the rows that could have changed, usually the last few days, and leaves the historical data alone because it is not going to change anyway. Last year's closed invoices are not going to suddenly report different totals. So why reload them.

Microsoft's reference on configuring incremental refresh walks through the mechanics. What I want to do here is give you the version that comes from actually setting this up on client models, including the parts the docs are quiet about and the mistakes that will eat an afternoon if nobody warns you.

What incremental refresh actually does

Under the hood, Power BI partitions your table by date. When you set up an incremental refresh policy, you are telling the engine two things: how far back to keep data, and how much of the recent data to refresh on each run. A common setup is "store the last 5 years, refresh the last 10 days". On the daily run, only those 10 days of partitions get reloaded. The other roughly 1,815 days of data sit untouched in partitions that were built once and never rebuilt.

The payoff is not subtle. A refresh that touched every row and took 45 minutes might now touch a sliver and finish in two or three. Your source system gets hammered less, your gateway does less work, and the whole thing is far less likely to fall over on a bad network day. For big models this is the difference between a platform people trust and one they quietly stop relying on.

There is a bonus most people miss. The first time the model refreshes after you publish, Power BI builds all the historical partitions once. After that, it is only ever touching the recent window. So the heavy lifting happens once, up front, and every run after that is light.

The two parameters that make it work

This is where people trip, so pay attention to this bit. Incremental refresh depends on two specially named parameters in Power Query: RangeStart and RangeEnd. They have to be named exactly that, they have to be of type Date/Time, and you have to use them to filter the date column on your fact table. Power BI uses these parameters as the hooks to slice your data into partitions.

The workflow is: create the two parameters, then filter your date column so it is greater than or equal to RangeStart and less than RangeEnd. In Power BI Desktop you give them some placeholder values so you can develop against a small slice of data, which is a nice side effect because you are not dragging the full dataset around while you build. Then you define the refresh policy on the table, publish, and the service takes over from there, swapping in real date ranges as it builds each partition.

Get the filter boundaries right. It is greater-than-or-equal on the start and strictly-less-than on the end. If you use less-than-or-equal on both, you will double-count rows that land exactly on a partition boundary. That is the kind of bug that produces slightly-wrong totals nobody notices until someone reconciles against the source and everything is off by a rounding-error amount that turns out not to be a rounding error at all.

Query folding is not optional here

Here is the thing the documentation mentions but does not shout loudly enough. Incremental refresh only works properly when your query folds. Query folding is when Power Query pushes your transformations back to the source as a native query, so the database does the filtering rather than Power BI pulling everything down and filtering locally.

If your date filter folds, the source database gets a query that says "give me rows where the date is in this window" and returns only those rows. Efficient, fast, exactly what you want. If your query does not fold, Power BI pulls the entire table across the wire and then filters it in memory, which defeats the entire purpose. You set up incremental refresh to avoid loading everything, and a broken fold means you are loading everything anyway, just with extra steps.

This is why incremental refresh sings against proper databases, SQL Server, Azure SQL, Synapse, Snowflake, and gets awkward against sources that do not fold well, like some flat files or certain web APIs. Before you build the policy, right-click your final applied step in Power Query and check whether "View Native Query" is available. If it is greyed out, your query has stopped folding somewhere and you need to find where and fix it before incremental refresh will do you any good. A stray step that changes column types in the wrong order, or a custom function, can quietly break folding and you will not get an error, just slow refreshes that make no sense.

Real-time and near-real-time data

Incremental refresh gets your scheduled refreshes fast, but scheduled is still scheduled. If the business needs numbers that update through the day rather than once overnight, that is a different tool.

Power BI has a few ways to get closer to live data. You can combine incremental refresh with a "get the latest data in real time" option, which uses DirectQuery for the most recent partition while keeping older data imported. So the bulk of your model is fast imported data, and only the very newest slice is queried live from the source. That hybrid approach is genuinely clever and works well when you need today's activity to be current but you are happy for last month to be a cached snapshot.

For truly live scenarios, streaming datasets and push datasets exist, and DirectQuery keeps everything live at the cost of query performance. My honest take: most businesses that ask for "real-time dashboards" do not actually need real-time. They need "current enough", and a model that refreshes every hour with incremental refresh feels live to a human looking at an operational dashboard. Real-time streaming has real use cases, IoT sensors, live logistics tracking, but reaching for it when hourly would do adds complexity and cost you will regret. Ask what decision the freshness actually changes. If nobody can act on data that is fifteen minutes fresher, you do not need fifteen-minute freshness.

The setup mistakes we see over and over

A few traps come up on nearly every engagement.

People set the historical window too wide out of habit. "Store the last 10 years" sounds safe, but if the business only ever looks back three years, you are carrying seven years of partitions that bloat the model and slow everything for no benefit. Size the window to what the reports actually use.

People forget that changing the incremental refresh policy after publishing can force a full reload. If you widen the historical range or change the partitioning, the service may need to rebuild everything, and that first refresh after the change is back to being the slow one. Plan those changes for a quiet time, not the morning of a board meeting.

And people set it up in Desktop, see it work against the small placeholder range, and assume they are done, without confirming the partitions actually built correctly in the service. Publish, run the first refresh, and check the timing and row counts against the source before you call it finished. The whole point is a behaviour that only fully happens in the service, so verify it there.

Where this fits

Incremental refresh is not glamorous, but it is one of the highest-return things you can do to a large Power BI model. It is the difference between a platform that refreshes reliably before people arrive and one that is still churning at 9am with everyone waiting. Most of the value is in getting the plumbing right once: folding queries, correct parameter filters, a sensibly sized window.

This is core to how we build reporting that holds up at scale in our Power BI consulting work, and it sits alongside the broader data engineering we do with Microsoft Fabric and Data Factory when the source side needs shaping before it even reaches the model. If you are wrestling with refresh times, or you want a model that behaves as your data grows rather than degrading, that is exactly the kind of problem we like. Happy to have a chat about your setup and where the time is actually going.