DAX and data models
The Complete Guide to Power BI Performance Optimization
How to make Power BI reports fast, layer by layer: query folding, star schema, relationships, DAX, report design and capacity, with the order to work in.
Sajagan Thirugnanam
·
Updated
Power BI performance optimization means making a report load, filter and refresh faster by fixing four layers in order: how data is retrieved, how the model is shaped, how measures are written, and how report pages are built. Most slow reports are fixed in the first two layers, by keeping query folding intact and building a star schema. Measure the problem before changing anything, and work down the layers in the order below.
What Power BI performance optimization really means
Optimization reduces five things:
Query execution time when a visual loads or a slicer changes.
Memory used by the model.
Model complexity, which drives both of the above.
Refresh duration.
Visual rendering time.
These are spread across four layers:
Layer | What it controls | Typical fix |
|---|---|---|
Data source | How much data arrives and how | Keep query folding, remove unused columns and rows |
Data model | Tables, relationships, column cardinality (the number of distinct values in a column) | Star schema, single-direction one-to-many relationships |
DAX | How measures compute | Column filters instead of table filters, variables |
Report | How many queries a page sends | Fewer visuals, fewer interactions |
Fixing one layer rarely solves the whole problem.
Why your Power BI report is slow
Before optimizing, find which layer is slow. Most causes fall into four groups:
Data retrieval. Query folding breaks, unused columns load, or DirectQuery scans large source tables.
Data modeling. Many-to-many relationships, snowflaked dimensions, or high-cardinality columns such as timestamps with seconds.
DAX. Iterators over large tables, repeated sub-expressions, or
FILTERover a whole table insideCALCULATE.Report design. Too many visuals per page, heavy custom visuals, or every visual cross-filtering every other.
Our slow report troubleshooting guide walks through diagnosing each one.
Query folding in Power BI explained
Query folding is Power Query translating your steps into one query that the source runs, such as a SQL statement. When it works, the source filters and aggregates, less data travels, and refresh is faster. When a step cannot fold, that step and every step after it run in Power BI, on all rows the source returned.
Common folding breakers:
Adding an index column.
Custom columns using functions the source has no equivalent for.
Merging queries from two different sources.
Put foldable steps first. In this query, the column selection and the date filter fold into SQL, and the index column is added last, where it can no longer stop earlier steps from folding:
let
Source = Sql.Database("myserver.database.windows.net", "SalesDW"),
Sales = Source{[Schema = "dbo", Item = "FactSales"]}[Data],
KeptColumns = Table.SelectColumns(Sales, {"OrderDate", "ProductKey", "CustomerKey", "SalesAmount"}),
RecentRows = Table.SelectRows(KeptColumns, each [OrderDate] >= #date(2024, 1, 1)),
AddedIndex = Table.AddIndexColumn(RecentRows, "Index", 1, 1, Int64.Type)
in
AddedIndexThis assumes OrderDate is a date column. To check folding, right-click a step in Applied steps. If View Native Query is available, the step folds.
Other data-loading fixes that belong here:
Remove columns you do not report on, at the source step.
Filter rows at the source, not after a merge.
Push heavy transformations upstream into a SQL view or a warehouse table.
Use incremental refresh for large fact tables.
Data model optimization
The model's shape decides performance before any DAX is written.
Star schema (recommended)
Fact tables hold events and numbers.
Dimension tables hold descriptive columns.
Each dimension relates directly to the fact tables with a one-to-many relationship.
Filters take the shortest route, columns compress well, and DAX stays simple.
Snowflake schema
Dimensions split into chains, such as Product, then Subcategory, then Category.
Filters travel through more relationships.
The model has more tables and more key columns.
Microsoft's guidance is that a single denormalized dimension table usually beats a snowflaked one in Power BI. See star schema vs snowflake schema in Power BI for the comparison and how to flatten one.
Power BI relationships and cardinality optimization
Relationships decide how filters move, and therefore how queries run:
Prefer one-to-many relationships.
Avoid many-to-many relationships where a bridge table or a proper dimension would work.
Relate on integer keys rather than long text keys.
Default to single-direction filtering. See cross filter direction for when Both is justified.
Cardinality, the number of distinct values in a column, drives memory use. A datetime column with seconds has far more distinct values than a date column. Split it into a date and a time column, or round it, if you do not need the precision.
Full explanation: Power BI relationships and cardinality.
DAX performance optimization: fixing slow measures
After the model, DAX is the next layer. Two patterns cover many slow measures.
Filter a column, not a table. The first measure below iterates every row of Sales. The second filters one column of Product, which the engine handles much more cheaply. Both return red-product sales within the current filters:
Red Sales Slow =
CALCULATE (
[Sales Amount],
FILTER ( Sales, RELATED ( 'Product'[Color] ) = "Red" )
)
Red Sales =
CALCULATE (
[Sales Amount],
KEEPFILTERS ( 'Product'[Color] = "Red" )
)Compute a value once. Variables store a result so the measure does not evaluate it twice:
Sales Growth % =
VAR CurrentSales = [Sales Amount]
VAR PriorSales =
CALCULATE ( [Sales Amount], SAMEPERIODLASTYEAR ( 'Date'[Date] ) )
RETURN
DIVIDE ( CurrentSales - PriorSales, PriorSales )Detailed guide: DAX performance optimization.
The Power BI performance optimization framework
Work in this order:
Verify query folding. Make the source do the filtering and shaping.
Fix the model. Build a star schema with clean one-to-many relationships.
Reduce cardinality. Remove unused columns and split or round high-cardinality ones.
Optimize DAX. Rewrite the slowest measures, found with Performance analyzer.
Improve report design. Cut visuals and interactions on the slowest pages.
The order matters. Tuning DAX on a badly shaped model rarely lasts, because the next measure hits the same problem.
Advanced techniques and newer storage options
Aggregation tables. A small, pre-summarized table answers most queries, and the detailed table is only queried when a visual needs the detail.
Hybrid tables. An incremental refresh policy can add a DirectQuery partition for the newest data on top of imported history. This needs Premium, Premium Per User or Embedded.
Composite models. Mix Import and DirectQuery tables, for example importing dimensions and leaving a very large fact table in DirectQuery. See Import vs DirectQuery.
Direct Lake. In Microsoft Fabric, a semantic model can read Delta tables in OneLake directly with the same in-memory engine that Import uses, without copying the data in a scheduled import refresh.
Each of these adds moving parts. Use them after the basics above are in place, not instead of them.
Environment and infrastructure optimization
A well-built model can still be slow if the environment around it is not sized for it:
Capacity. On Premium or Fabric capacity, heavy refreshes and heavy report use share the same resources. Schedule large refreshes away from peak viewing times.
Gateways. An on-premises data gateway needs enough CPU and memory for concurrent refreshes, and should sit close to the source on the network.
Network latency. DirectQuery sends a query per visual, so distance between Power BI and the source adds to every page load.
Storage mode. Import is fastest to query. DirectQuery is only as fast as the source database.
Performance measurement and monitoring
Measure before and after every change:
Performance analyzer in Power BI Desktop, on the Optimize ribbon, records how long each visual spends on its DAX query, on rendering, and on other work.
DAX Studio, a free external tool, runs the query copied from Performance analyzer and shows server timings.
Refresh history in the semantic model settings shows refresh duration and failures over time.
The Microsoft Fabric Capacity Metrics app shows capacity usage and throttling on Premium and Fabric capacities.
Track visual load time, query duration, refresh time and model size. A regression is much easier to fix the week it appears.
Report and visualization optimization
Every visual on a page sends its own query. A page with many visuals sends many queries every time a slicer changes.
Keep each page to the visuals its reader needs, and move detail to drill-through pages.
Remove slicers nobody uses.
Turn off visual interactions that add nothing, under Format > Edit interactions.
Set default filters that limit data, such as the current year.
Test custom visuals in Performance analyzer before keeping them.
Governance and best practices
Performance degrades as more people build on a model. Standards keep it from drifting:
Modeling conventions: star schema, integer keys, single-direction relationships.
Naming standards for tables and measures.
A note of why each exception was made, such as a bidirectional relationship.
A named owner for each shared semantic model.
A consistent deployment process between development, test and production.
Row-level security rules also run on every query. Keep them simple, and filter dimension tables rather than large fact tables where possible.
Common performance myths
"Power BI is slow with large datasets"
Row count alone rarely decides speed. A large fact table in a clean star schema with low-cardinality columns can respond quickly, while a small model with many-to-many relationships can be slow.
"We just need better hardware"
A larger capacity helps when the capacity is overloaded. It does not fix a query that scans every row because of a broken relationship or a table-level FILTER.
"DAX is the main problem"
DAX is often where the symptom shows. The cause is frequently the model underneath, which forces the measure to do extra work.
Performance optimization checklist
Query folding holds through the filter steps.
Model is a star schema.
Relationships are one-to-many and single-direction, with documented exceptions.
High-cardinality columns are removed, split or rounded.
Measures use variables and column filters.
Unused columns are removed.
Each page has only the visuals its reader needs.
If several items fail, start with the first one that does.
How CaseWhen helps organizations fix slow Power BI reports
CaseWhen diagnoses slow Power BI reports layer by layer and fixes the cause, whether that is the source queries, the model or the measures. We also redesign models and capacity setups for teams whose usage has outgrown the original build.
FAQs
Why is my Power BI report slow?
The most common causes are a model that is not a star schema, query folding that breaks early, DAX that iterates large tables, and pages with too many visuals. Open Performance analyzer in Power BI Desktop to see whether the time goes into DAX queries or rendering, then work from there.
What improves Power BI performance the most?
Usually the model. Moving to a star schema with one-to-many, single-direction relationships and removing high-cardinality columns tends to help every visual at once. Query folding matters most for refresh time.
Should I optimize the model or the DAX first?
The model. How fast a measure runs depends heavily on the model underneath it: a star schema, fewer and narrower columns, and single-direction relationships speed up every measure at once. Once the model is sound, use Performance Analyzer to find the slowest measures and tune those.
Sources
Use Performance Analyzer to examine report element performance - Microsoft Learn
Understand star schema and the importance for Power BI - Microsoft Learn
Optimization guide for Power BI - Microsoft Learn
Query folding basics - Microsoft Learn
Power BI performance optimization means making a report load, filter and refresh faster by fixing four layers in order: how data is retrieved, how the model is shaped, how measures are written, and how report pages are built. Most slow reports are fixed in the first two layers, by keeping query folding intact and building a star schema. Measure the problem before changing anything, and work down the layers in the order below.
What Power BI performance optimization really means
Optimization reduces five things:
Query execution time when a visual loads or a slicer changes.
Memory used by the model.
Model complexity, which drives both of the above.
Refresh duration.
Visual rendering time.
These are spread across four layers:
Layer | What it controls | Typical fix |
|---|---|---|
Data source | How much data arrives and how | Keep query folding, remove unused columns and rows |
Data model | Tables, relationships, column cardinality (the number of distinct values in a column) | Star schema, single-direction one-to-many relationships |
DAX | How measures compute | Column filters instead of table filters, variables |
Report | How many queries a page sends | Fewer visuals, fewer interactions |
Fixing one layer rarely solves the whole problem.
Why your Power BI report is slow
Before optimizing, find which layer is slow. Most causes fall into four groups:
Data retrieval. Query folding breaks, unused columns load, or DirectQuery scans large source tables.
Data modeling. Many-to-many relationships, snowflaked dimensions, or high-cardinality columns such as timestamps with seconds.
DAX. Iterators over large tables, repeated sub-expressions, or
FILTERover a whole table insideCALCULATE.Report design. Too many visuals per page, heavy custom visuals, or every visual cross-filtering every other.
Our slow report troubleshooting guide walks through diagnosing each one.
Query folding in Power BI explained
Query folding is Power Query translating your steps into one query that the source runs, such as a SQL statement. When it works, the source filters and aggregates, less data travels, and refresh is faster. When a step cannot fold, that step and every step after it run in Power BI, on all rows the source returned.
Common folding breakers:
Adding an index column.
Custom columns using functions the source has no equivalent for.
Merging queries from two different sources.
Put foldable steps first. In this query, the column selection and the date filter fold into SQL, and the index column is added last, where it can no longer stop earlier steps from folding:
let
Source = Sql.Database("myserver.database.windows.net", "SalesDW"),
Sales = Source{[Schema = "dbo", Item = "FactSales"]}[Data],
KeptColumns = Table.SelectColumns(Sales, {"OrderDate", "ProductKey", "CustomerKey", "SalesAmount"}),
RecentRows = Table.SelectRows(KeptColumns, each [OrderDate] >= #date(2024, 1, 1)),
AddedIndex = Table.AddIndexColumn(RecentRows, "Index", 1, 1, Int64.Type)
in
AddedIndexThis assumes OrderDate is a date column. To check folding, right-click a step in Applied steps. If View Native Query is available, the step folds.
Other data-loading fixes that belong here:
Remove columns you do not report on, at the source step.
Filter rows at the source, not after a merge.
Push heavy transformations upstream into a SQL view or a warehouse table.
Use incremental refresh for large fact tables.
Data model optimization
The model's shape decides performance before any DAX is written.
Star schema (recommended)
Fact tables hold events and numbers.
Dimension tables hold descriptive columns.
Each dimension relates directly to the fact tables with a one-to-many relationship.
Filters take the shortest route, columns compress well, and DAX stays simple.
Snowflake schema
Dimensions split into chains, such as Product, then Subcategory, then Category.
Filters travel through more relationships.
The model has more tables and more key columns.
Microsoft's guidance is that a single denormalized dimension table usually beats a snowflaked one in Power BI. See star schema vs snowflake schema in Power BI for the comparison and how to flatten one.
Power BI relationships and cardinality optimization
Relationships decide how filters move, and therefore how queries run:
Prefer one-to-many relationships.
Avoid many-to-many relationships where a bridge table or a proper dimension would work.
Relate on integer keys rather than long text keys.
Default to single-direction filtering. See cross filter direction for when Both is justified.
Cardinality, the number of distinct values in a column, drives memory use. A datetime column with seconds has far more distinct values than a date column. Split it into a date and a time column, or round it, if you do not need the precision.
Full explanation: Power BI relationships and cardinality.
DAX performance optimization: fixing slow measures
After the model, DAX is the next layer. Two patterns cover many slow measures.
Filter a column, not a table. The first measure below iterates every row of Sales. The second filters one column of Product, which the engine handles much more cheaply. Both return red-product sales within the current filters:
Red Sales Slow =
CALCULATE (
[Sales Amount],
FILTER ( Sales, RELATED ( 'Product'[Color] ) = "Red" )
)
Red Sales =
CALCULATE (
[Sales Amount],
KEEPFILTERS ( 'Product'[Color] = "Red" )
)Compute a value once. Variables store a result so the measure does not evaluate it twice:
Sales Growth % =
VAR CurrentSales = [Sales Amount]
VAR PriorSales =
CALCULATE ( [Sales Amount], SAMEPERIODLASTYEAR ( 'Date'[Date] ) )
RETURN
DIVIDE ( CurrentSales - PriorSales, PriorSales )Detailed guide: DAX performance optimization.
The Power BI performance optimization framework
Work in this order:
Verify query folding. Make the source do the filtering and shaping.
Fix the model. Build a star schema with clean one-to-many relationships.
Reduce cardinality. Remove unused columns and split or round high-cardinality ones.
Optimize DAX. Rewrite the slowest measures, found with Performance analyzer.
Improve report design. Cut visuals and interactions on the slowest pages.
The order matters. Tuning DAX on a badly shaped model rarely lasts, because the next measure hits the same problem.
Advanced techniques and newer storage options
Aggregation tables. A small, pre-summarized table answers most queries, and the detailed table is only queried when a visual needs the detail.
Hybrid tables. An incremental refresh policy can add a DirectQuery partition for the newest data on top of imported history. This needs Premium, Premium Per User or Embedded.
Composite models. Mix Import and DirectQuery tables, for example importing dimensions and leaving a very large fact table in DirectQuery. See Import vs DirectQuery.
Direct Lake. In Microsoft Fabric, a semantic model can read Delta tables in OneLake directly with the same in-memory engine that Import uses, without copying the data in a scheduled import refresh.
Each of these adds moving parts. Use them after the basics above are in place, not instead of them.
Environment and infrastructure optimization
A well-built model can still be slow if the environment around it is not sized for it:
Capacity. On Premium or Fabric capacity, heavy refreshes and heavy report use share the same resources. Schedule large refreshes away from peak viewing times.
Gateways. An on-premises data gateway needs enough CPU and memory for concurrent refreshes, and should sit close to the source on the network.
Network latency. DirectQuery sends a query per visual, so distance between Power BI and the source adds to every page load.
Storage mode. Import is fastest to query. DirectQuery is only as fast as the source database.
Performance measurement and monitoring
Measure before and after every change:
Performance analyzer in Power BI Desktop, on the Optimize ribbon, records how long each visual spends on its DAX query, on rendering, and on other work.
DAX Studio, a free external tool, runs the query copied from Performance analyzer and shows server timings.
Refresh history in the semantic model settings shows refresh duration and failures over time.
The Microsoft Fabric Capacity Metrics app shows capacity usage and throttling on Premium and Fabric capacities.
Track visual load time, query duration, refresh time and model size. A regression is much easier to fix the week it appears.
Report and visualization optimization
Every visual on a page sends its own query. A page with many visuals sends many queries every time a slicer changes.
Keep each page to the visuals its reader needs, and move detail to drill-through pages.
Remove slicers nobody uses.
Turn off visual interactions that add nothing, under Format > Edit interactions.
Set default filters that limit data, such as the current year.
Test custom visuals in Performance analyzer before keeping them.
Governance and best practices
Performance degrades as more people build on a model. Standards keep it from drifting:
Modeling conventions: star schema, integer keys, single-direction relationships.
Naming standards for tables and measures.
A note of why each exception was made, such as a bidirectional relationship.
A named owner for each shared semantic model.
A consistent deployment process between development, test and production.
Row-level security rules also run on every query. Keep them simple, and filter dimension tables rather than large fact tables where possible.
Common performance myths
"Power BI is slow with large datasets"
Row count alone rarely decides speed. A large fact table in a clean star schema with low-cardinality columns can respond quickly, while a small model with many-to-many relationships can be slow.
"We just need better hardware"
A larger capacity helps when the capacity is overloaded. It does not fix a query that scans every row because of a broken relationship or a table-level FILTER.
"DAX is the main problem"
DAX is often where the symptom shows. The cause is frequently the model underneath, which forces the measure to do extra work.
Performance optimization checklist
Query folding holds through the filter steps.
Model is a star schema.
Relationships are one-to-many and single-direction, with documented exceptions.
High-cardinality columns are removed, split or rounded.
Measures use variables and column filters.
Unused columns are removed.
Each page has only the visuals its reader needs.
If several items fail, start with the first one that does.
How CaseWhen helps organizations fix slow Power BI reports
CaseWhen diagnoses slow Power BI reports layer by layer and fixes the cause, whether that is the source queries, the model or the measures. We also redesign models and capacity setups for teams whose usage has outgrown the original build.
FAQs
Why is my Power BI report slow?
The most common causes are a model that is not a star schema, query folding that breaks early, DAX that iterates large tables, and pages with too many visuals. Open Performance analyzer in Power BI Desktop to see whether the time goes into DAX queries or rendering, then work from there.
What improves Power BI performance the most?
Usually the model. Moving to a star schema with one-to-many, single-direction relationships and removing high-cardinality columns tends to help every visual at once. Query folding matters most for refresh time.
Should I optimize the model or the DAX first?
The model. How fast a measure runs depends heavily on the model underneath it: a star schema, fewer and narrower columns, and single-direction relationships speed up every measure at once. Once the model is sound, use Performance Analyzer to find the slowest measures and tune those.
Sources
Use Performance Analyzer to examine report element performance - Microsoft Learn
Understand star schema and the importance for Power BI - Microsoft Learn
Optimization guide for Power BI - Microsoft Learn
Query folding basics - Microsoft Learn
Show us the report nobody trusts.
1 · A 30-minute call.
2 · We look at your current reports together.
3 · We tell you what we would do.
Show us the report nobody trusts.
1 · A 30-minute call.
2 · We look at your current reports together.
3 · We tell you what we would do.
Show us the report nobody trusts.
1 · A 30-minute call.
2 · We look at your current reports together.
3 · We tell you what we would do.
CaseWhen is a Berlin BI consultancy that builds reporting that leaders can trust, on the Microsoft stack: Power BI, Fabric and Azure.
Berlin, Germany
Free tools
CaseWhen is a Berlin BI consultancy that builds reporting that leaders can trust, on the Microsoft stack: Power BI, Fabric and Azure.
Berlin, Germany
Free tools
CaseWhen is a Berlin BI consultancy that builds reporting that leaders can trust, on the Microsoft stack: Power BI, Fabric and Azure.
Berlin, Germany
