Calculated vs Rollup: Stop Picking Wrong
Calculated columns evaluate formulas in real time. Rollup columns aggregate child records on a schedule. Here's when to use each — and when to skip both.
Dataverse gives you two types of columns that compute values for you: calculated and rollup. They sound similar, but they solve different problems in very different ways. Picking the wrong one wastes time and creates confusion. Picking the right one saves you from writing code you don’t need.
What Calculated Columns Do
A calculated column evaluates a formula in real time whenever the record is read. The formula runs against data on the current record (and one level of lookup). The result is always up to date because it’s computed on the fly.
Common examples:
- Days since created:
diffindays(createdon, now()) - Full name:
firstname + " " + lastname - Due in X days:
adddays(createdon, 30) - Status flag: If priority equals High and due date is in the past, set to “Overdue”
Calculated columns are good for transforming, formatting, or deriving values from fields that already exist on the record. They show up immediately — no waiting, no refresh.
What Rollup Columns Do
A rollup column aggregates data from related child records. It looks across a one-to-many relationship, runs a filter, and computes a SUM, COUNT, AVG, MIN, or MAX.
Common examples:
- Total revenue: SUM of Estimated Revenue on child Opportunity records
- Active case count: COUNT of related Cases where Status equals Active
- Latest activity date: MAX of Modified On across related Activity records
- Average deal size: AVG of Amount on related closed-won Opportunities
The key difference: rollup columns don’t compute in real time. They run on a scheduled job every 12 hours. Between refreshes, the displayed value can be stale.
Forcing a Rollup Refresh
Users can click the refresh icon next to a rollup column on a form to recalculate it on demand. You can also trigger a refresh through code using the CalculateRollupField function in the Web API or SDK. But the system-wide scheduled recalculation runs every 12 hours, and there’s no way to change that interval.
If you need the number to always be current the moment a record loads, rollup columns are not the right tool.
Limitations of Calculated Columns
Calculated columns have guardrails that catch people off guard:
- One level of lookup only. You can reference a lookup field’s columns (e.g.,
parentcustomerid.name), but you can’t chain lookups. Noparentcustomerid.primarycontactid.emailaddress1. If you need data from two relationships deep, a calculated column can’t reach it. - No aggregation. A calculated column operates on the current record. It cannot count, sum, or query related records. That’s what rollup columns are for.
- Limited function set. You get DIFFINDAYS, ADDDAYS, CONCAT, IF/THEN, and a few others. No string parsing, no regex, no advanced math.
- Cannot reference other calculated or rollup columns. You can’t chain calculated columns together. A calculated column can’t use the output of another calculated column as input.
- No cross-entity writes. Calculated columns are read-only computations. They don’t update other records.
- Filtered views and reports. Calculated columns that use
now()behave differently in views versus forms. The value is computed at query time, which can produce unexpected sorting and filtering results in large datasets.
Limitations of Rollup Columns
- 12-hour refresh cycle. The calculated value can be up to 12 hours old at any given time. For dashboards or reports where the exact number matters right now, this is a problem.
- Limited aggregate functions. SUM, COUNT, AVG, MIN, MAX. No MEDIAN, no weighted averages, no custom aggregation logic.
- One relationship deep. Rollup columns aggregate direct child records. You can’t roll up data from grandchild records (e.g., sum values from line items on orders that belong to an account).
- Filter limitations. The filter on which child records to include supports basic conditions, but you can’t build anything complex. No OR groups, no related entity filters within the rollup filter.
- Performance at scale. If a parent record has thousands of child records, the rollup recalculation takes longer. In large datasets, this affects system job throughput.
- Max rollup columns per table. Dataverse limits you to 15 rollup columns per table. Plan accordingly.
When to Use Which
| Scenario | Use |
|---|---|
| Derive a value from fields on the current record | Calculated |
| Concatenate or format existing fields | Calculated |
| Age calculation (days since created, days until due) | Calculated |
| Conditional flag based on current record’s fields | Calculated |
| Total or count from child records | Rollup |
| Average or min/max across related records | Rollup |
| The value can be slightly stale and that’s acceptable | Rollup |
| You need a real-time aggregate that’s always current | Neither — use a plugin |
When to Skip Both and Use a Plugin
Here’s where I get opinionated: calculated and rollup columns are configuration tools. They handle common cases well. But if your requirement doesn’t fit neatly into their constraints, don’t force it. Write a plugin instead.
Use a plugin when:
- You need real-time aggregation. A customer calls in and the agent needs to see the exact total right now, not the value from 6 hours ago. A pre- or post-operation plugin on the child record can update a number field on the parent immediately.
- You need multi-level lookups. The value depends on data two or more relationships away. Calculated columns can’t reach it. A plugin can query whatever it needs.
- The logic is conditional and complex. Multiple branches, lookups to unrelated tables, calculations that depend on configuration data stored elsewhere. The calculated column formula editor will fight you the entire way.
- You need to write values to other records. Calculated columns are read-only. Rollup columns are read-only. If computing a value should trigger an update somewhere else, that’s code.
- You need the calculation to run inside the transaction. Calculated columns compute on read. Rollup columns compute on a schedule. Neither participates in the save transaction. If the calculation must succeed or fail with the rest of the operation, it belongs in a plugin registered on the pipeline.
A Power Automate flow can also handle some of these cases, but a plugin runs synchronously inside the transaction and doesn’t depend on an external service being available. For business-critical calculations, a plugin is the more reliable choice.
The Decision in 30 Seconds
Ask yourself three questions:
- Does the value come from fields on this record (or one lookup away)? Use a calculated column.
- Does the value aggregate child records, and is a 12-hour delay acceptable? Use a rollup column.
- Is the answer “no” to both? Write a plugin.
Calculated and rollup columns exist so you don’t have to write code for straightforward scenarios. Use them for that. The moment your requirement outgrows their constraints, stop trying to make them work and build a proper solution instead.
Related articles
Cascading Dropdowns in Canvas Apps (Yes, It's Harder Than It Should Be)
A parent dropdown for Country, a child dropdown for City that only shows cities in the selected country. Here's the pattern, the delegation-safe version, and the reset behavior that catches everyone.
Lookup Columns in Power Apps: Create, Display, and Patch Without the Guesswork
Lookups are how Dataverse tables relate to each other, and they're the column type that causes the most confusion in Power Apps. Here's how to set them up and work with them in canvas apps.
Get the Current User in Power Apps
The User() function gets you started, but most real scenarios need more. Here's how to get the current user's details from Dataverse, including Business Unit, Team membership, and security role.