About incremental refresh
Incremental refresh in Power BI divides a table into date ranges called partitions and reloads only the most recent ones on each refresh. For example, you can keep one year of data and reload only the last seven days. This shortens refreshes and lowers the risk of timeouts.Any table with a timestamp column
You can use incremental refresh with data model instances, records, data points, and other CDF resources that support filtering by timestamp. The table needs one date and time column that places each row in time. Power BI uses that column to sort every row into a time range.The timestamp column decides which time range each row belongs to
Why a full import fails on large data
Without incremental refresh, every refresh downloads the whole table. Two limits apply: how fast CDF can return data, and how long Power BI waits before it cancels the refresh. If the download takes longer than the refresh timeout, Power BI cancels the refresh and saves nothing. A faster connection doesn’t solve this. Request less data in each query instead.A large history runs past the refresh timeout, while one week finishes well within it
How partitions work
Power BI fetches each partition separately and passes its start and end to your query as theRangeStart and RangeEnd parameters. On each scheduled refresh, it downloads only the most recent partitions again and keeps the older ones as they are.
Each refresh runs your query once for each reloaded partition, with that partition's RangeStart and RangeEnd
Before you start
- Edit queries in the Power Query editor, including parameters and basic M code.
- Publish a model to the Power BI Service and configure scheduled refresh.
- Create calculated tables with DAX. You need this only to backfill history or handle updates and deletions.
- Make sure the endpoint you want to fetch data from supports timestamp-based filtering.
- Your queries must return the same column names and data types, even when a date range contains no rows.
- All date ranges in an incrementally refreshed table must read from the same data source.
Set up incremental refresh
Create the parameters
datetime parameters named RangeStart and RangeEnd. Set them to a short period, such as one day, so Power BI Desktop loads only a small sample while you develop. After you publish, the Power BI Service sets the values for each partition. The Power BI template includes both parameters.Filter the query
RangeStart and RangeEnd. Use the utility functions to convert the parameters to the format CDF expects, and start from one of the examples.Load the data
Configure the incremental refresh policy
Publish and refresh
Plan the first refresh
The first refresh after you publish loads the whole history you chose to keep. It’s the only refresh that must fit all of it inside the refresh timeout.The first refresh downloads the whole history. Later refreshes download only the most recent partitions
- At 900 bytes per row (all columns), a day is about 6.3 GB. At an assumed effective throughput of 0.83 MB/s, that’s about 2.1 hours per day before model processing. Seven days take about 15 hours, which is likely to exceed the refresh timeout.
- At 360 bytes per row (only the columns you need), a day is about 2.5 GB and takes roughly 50 minutes at the same throughput, before model processing.
- Ask CDF for only the rows and columns your report needs. See Filter in CDF, not in Power BI.
- Keep less history in the incremental refresh policy.
- On Premium or Fabric, use the XMLA endpoint options for bootstrapping large initial loads.
Filter in CDF, not in Power BI
Where you filter decides how much data Power BI downloads from CDF. If you remove rows or columns in a Power Query step after the connector function returns, Power BI has already downloaded them, and the refresh takes just as long. To download less data, include the filter and the columns you need in the request that CDF receives. This is called server-side filtering. Each connector function has its own place for this:RangeStart and RangeEnd filter follows the same rule. Put it in the request, as the examples on this page do, so each partition downloads only its own time range.
Backfill large histories outside Power BI
When the history is too large for the first refresh, export the older data once with the API and save it to files. Load those files into Power BI once, outside the scheduled refresh, so the refresh timeout doesn’t apply. Then set up incremental refresh only for the data after the cutoff date where the files end.History before the cutoff date is loaded from files. Incremental refresh handles the data after it
AllRows as a calculated table:
UNION matches columns by position, so both tables must have the same columns in the same order. Filter RecentRows with >= on the cutoff date and the files with < on the same date, so no row appears in both tables.
Handle updates and deletions
The timestamp column you partition by decides whether changes to existing items reach your report:Keep the latest version of each item
Add a calculated table that keeps, for each item, only the row with the highestlastUpdatedTime. Use the columns that identify an item as the key in ALLEXCEPT, for example externalId, or space and externalId for data modeling instances:
Items with the name of your incrementally refreshed table. If you backfill the history, use AllRows instead so the files and recent data are deduplicated together. Use LatestItems in your reports.
Exclude deleted items
Some endpoints return a deletion marker, also called a tombstone, when an item is deleted. A tombstone is a row with the item’s key, a newerlastUpdatedTime, and a field that marks it as deleted. When the latest row for an item is a tombstone, exclude the item. Add the condition to the same filter, and replace status and "deleted" with the field and value your endpoint uses:
Utility functions
Power BI suppliesRangeStart and RangeEnd as plain datetime values that represent UTC. Since CDF endpoints expect timestamps in specific formats, convert them to the format each request type expects:
- GetCDF/PostCDF: Normal GET/POST requests to the Cognite APIs expect timestamps in milliseconds since epoch (UTC).
- GraphQL: Data modeling GraphQL requests expect timestamps in ISO 8601 string format.
-
ODataCDF/ODataDataModel: OData requests expect timestamps in
datetimezoneformat for column filtering.
RangeStart and RangeEnd follow that time zone instead, and these functions no longer match CDF. Keep the default to avoid the mismatch. See Configure incremental refresh.ConvertDateTimeToMs
ConvertDateTimeToIso
ConvertDateTimeToDateTimeZone
EnforceSchema
EnforceSchema returns a table with the columns you list, even when the API response is empty. Missing columns are added with null values.
ColumnNamesToText
ColumnNamesToText writes the column names of a table as an M list that you can paste into EnforceSchema. Run it once on a GetCDF, PostCDF, or GraphQL response that returns at least one row.
EnforceSchema preserves existing column types and adds missing columns with null values. Apply explicit types after it, as shown below, so empty and populated partitions have the same schema.
Incremental refresh examples
These examples usePostCDF, GraphQL, and ODataDataModel. There is no GetCDF example, because you read data model instances and records through POST requests, GraphQL, or OData, not through GET endpoints with timestamp filters.
Add ConvertMsToUtcDateTime for the POST example.
Use an inclusive start and exclusive end (RangeStart <= timestamp < RangeEnd) so adjacent partitions neither overlap nor omit boundary rows. Nested fields such as properties are returned as JSON text for a stable, loadable schema. Expand them into explicitly typed columns instead if you need to report on individual properties.
The PostCDF and GraphQL examples partition by lastUpdatedTime, so an updated item loads again into a newer partition and the table can hold several rows for it. To keep only the latest row for each item, see Handle updates and deletions. The ODataDataModel example partitions by a view property instead, because the service can’t filter on lastUpdatedTime.
PostCDF example
This example shows an incremental refresh when fetchingCogniteActivity instances with the PostCDF function:
GraphQL example
This example shows how to implement an incremental refresh with a GraphQL query forCogniteActivity instances:
ODataDataModel example
This example shows incremental refresh withODataDataModel using column-level filtering, which Power BI can push down to the server. Note that when using OData you don’t need to enforce any schema since the OData service always returns a table with the same schema, even if no data is returned.
Rows with no startTime aren’t in any partition, and changes to older activities aren’t captured after their partitions stop refreshing.
- Microsoft: Incremental refresh and real-time data for semantic models
- Microsoft: Configure incremental refresh for semantic models
- Microsoft: Troubleshoot incremental refresh