Almanac
Microsoft/power-biPower Platform

Consultant KB for Microsoft Power BI: semantic models, DAX and modelling, Power Query and dataflows, reports and visuals, the service and workspaces, capacity and performance, embedding and integration, governance and security, ALM and deployment, and licensing, plus cross-cutting decision guides. Scoped to Power BI, with Microsoft Fabric covered where it touches Power BI directly. Implementation notes, configuration decisions and the gotchas that bite on real projects. Populated by the daily author agent from the Power BI release plans, docs repo and product blog, plus the author's own consultant notes.

feature-dax-performance-patterns.mdv2 · history
CurrentApplies to BothUpdated last weekSource Microsoft Learn

What it does

A small set of habits that keeps DAX fast as models grow: assign repeated expressions to variables, keep iterators off large tables, let the storage engine do the work, and measure rather than guess. Most slow models are slow for two or three identifiable reasons, not because DAX is inherently slow.

Key facts

  • VAR evaluates an expression once and reuses the result. Microsoft's own worked example — a year-over-year growth measure that computed the prior-year figure twice — runs in about half the query time once the repeated expression becomes a variable.
  • Variables are always evaluated outside the filters applied by the RETURN expression. That's why a variable replaces EARLIER and EARLIEST cleanly, and why those functions rarely belong in new code.
  • Returning a variable instead of the intended expression, with the real RETURN commented out, is the standard debugging technique. It's crude and it works.
  • Referencing a measure inside an iterator wraps it in an implicit CALCULATE and triggers context transition for every row. Over a fact table that's a filter context per row.
  • DIVIDE handles division by zero without a wrapping IF and is the intended way to write a ratio.
  • Aggregation functions that the storage engine can push down — SUM, COUNT, MIN, MAX, COUNTROWS — are far cheaper than the equivalent expressed as an iterator over the same table.
  • Performance Analyzer in Power BI Desktop shows the DAX query, visual render and other timings per visual, and lets you copy the generated query out.
  • DAX query view in Desktop runs DAX queries against the model directly, so you can test a measure in isolation rather than through a visual.
  • DAX Studio is the community external tool most consultants use for server timings and query plans. It's third-party, not a Microsoft product, and connects through the same interfaces Power BI exposes to external tools.

When to use / skip

Apply the variable habit and the "don't iterate a fact table" rule from the first measure — they cost nothing and they're impossible to retrofit cheaply across a mature model. Hold off on query plan analysis until you have an actual complaint with a number attached. Optimising a measure nobody waits for is a way of avoiding the model design problem that's really causing the slowness, which is usually a missing star schema, an oversized column, or bi-directional filtering somebody added eighteen months ago.

Configuration decisions

  • Whether the team's standard is variables for anything referenced twice, which is a code review rule rather than a preference.
  • Whether performance work starts at the model, the DAX or the report — nearly always in that order.
  • Which tooling the team is licensed and permitted to use, given DAX Studio is third-party and some clients restrict external tools.
  • What "acceptable" means numerically for this client, agreed up front, so performance work has a finish line.
  • Whether measures are reviewed before publish, or fixed reactively after users complain.

Gotchas

  • SUMX over a fact table calling a measure is the classic slow pattern, and it's often written by someone who was told SUMX is "more powerful" than SUM.
  • Nested iterators multiply. An iterator inside an iterator over two large tables can generate work that no amount of capacity fixes.
  • Slow visuals frequently aren't DAX at all — a table with forty thousand rows and no filters, or twenty card visuals on one page, will feel slow with perfect measures.
  • Performance Analyzer timings include cached results. Clear the cache between runs or you'll optimise something that was already warm.
  • A measure that's fast in isolation can be slow in a matrix, because the matrix asks for it once per cell plus totals. Test in the visual that's actually complained about.
  • Calculated columns on large tables don't show up in query timings at all — they cost memory and refresh time instead, and they're invisible to the tools people reach for first.

Consultant notes

  • Get a baseline before touching anything. Performance Analyzer output on the three slowest pages, saved, so you can show the improvement rather than assert it.
  • Resist the urge to lead with DAX tuning. Model size and design fixes usually deliver more, faster, and they're easier to explain to a non-technical sponsor.
  • If you use DAX Studio, check the client's policy on external tools first. Turning up with an unapproved executable on a locked-down laptop is an awkward morning.
  • Leave the client with the diagnosis, not just the fix. "This was slow because measures iterate the fact table" is a lesson their team can apply; a rewritten measure isn't.
  • Set a performance target in the statement of work. Without one, "make it faster" has no end.

Sticky note: before optimising a measure, check whether the model design is the actual problem.

Was this accurate?