3.1 · CALCULATE, filter context & variables (VAR)

Level 3 · Advanced analysis

3.1CALCULATE, filter context & variables (VAR)

Goal: Read a visual cell's context out loud, write the "% of total" with CALCULATE + ALL + VAR, and diagnose a DAX result through its context.
Estimated time: 25 min · self-paced reading · If you only have 5 min: read “The essentials in 30s” + the diagram.

The essentials in 30 seconds

  • Every cell of a visual has its own filter context: the "Paris × January" cell of a matrix computes [Total Sales] filtered on Paris AND January, without your writing anything.
  • CALCULATE modifies that context: it adds a filter, overrides one, or removes one with ALL — the key to "% of total".
  • VAR/RETURN structure any formula longer than one line: readability, performance and easier debugging.

Running mini-case — Zineb reads her cells out loud in Algiers

Zineb, a management controller in an agri-food SME in Algiers, displays a City × Month matrix of her [Total Sales] and is puzzled by a cell that looks wrong. She applies the section's method: instead of rewriting the measure, she reads the context out loud — "this cell = sales filtered on Lyon AND January" — and understands that each cell has its own. She then adds Sales All cities = CALCULATE([Total Sales]; ALL(Sales[City])): the same value everywhere, the city filter removed. Then she builds each city's share with a variable: Share city = VAR Total = CALCULATE([Total Sales]; ALL(Sales[City])) RETURN DIVIDE([Total Sales]; Total). She tests with an active Salesperson slicer: the share recalculates within the slicer's scope — exactly what she wants to explain to her management.

Standalone case for this section: it returns in the course, the diagram, the script and the 48h challenge of section 3.1.

1. The filter context

Here is the chapter that separates the user from the DAX practitioner. Every calculation is evaluated in a filter context: the "Paris × January" cell of a matrix computes [Total Sales] filtered on Paris AND January — without your writing anything. Every cell of a visual has its own: axes, legends, slicers and filters add to it.

2. CALCULATE, ALL and VAR

CALCULATE MODIFIES that context: it adds a filter, overrides one (the share of total) or removes one with ALL. ALL(Sales[City]) ignores the city filter and gives the grand total — it is the key to the most requested calculation: the "% of total".

3. Structuring and diagnosing

We wrap it all in VAR/RETURN variables: the overall total is calculated once, the formula reads in one go and debugging becomes simple. The diagnostic reflex fits in one sentence: a "strange" DAX result is almost always a misunderstood context — Zineb reads it out loud before touching the formula.

Common misinterpretation: "DAX is formulas to recite; a wrong result hides a syntax error, and the context changes nothing." No, triple mistake. A "strange" DAX result is almost always a misunderstood filter context: you read it out loud before anything else. CALCULATE does not calculate "more": it changes the context — it adds, overrides or removes (with ALL) a filter. And any formula longer than one line is structured with VAR/RETURN, otherwise it becomes unreadable and repeats its calculations.
Modifying the filter context Read the context, modify it with CALCULATE, reduce it with ALL — and structure it with VAR. Filter contextWhat the cell filters (to read)+CALCULATEAdds / overrides a filter+ALLRemoves filters (% of total) VAR/RETURN as soon as a formula is longer than one line.
Read the context, modify it with CALCULATE, reduce it with ALL — and structure it with VAR.
Text version of the diagram (SEO / screen readers / translation): 1) Zineb, a management controller in Algiers, reads the context: the "Lyon × January" cell of her matrix = [Total Sales] filtered on Lyon AND January, without her writing anything. 2) She modifies it: CALCULATE([Total Sales]; ALL(Sales[City])) removes the city filter and returns the total for all cities; assembled in a variable, it gives each city's share of the total. 3) She structures with VAR/RETURN — the overall total calculated once, a readable formula, diagnosis through the context. Lesson: a strange result always starts with a misunderstood context that you read out loud.

🔎 Concrete example — 🔎 Concrete example — The % of total

The most requested calculation, in short, readable formulas:

Revenue All Cities = CALCULATE([Total Revenue], ALL(Sales[City]))

City Share =
VAR Total = CALCULATE([Total Revenue], ALL(Sales[City]))
RETURN DIVIDE([Total Revenue], Total)   // formatted as %

Reading: ALL(Sales[City]) removes the city filter: the measure displays the same value on every row — the total for all cities.

Lesson: DIVIDE([Total Sales]; Total) gives each city's share, recalculated within the scope of the active slicers.

Section vocabulary

Filter context
The set of filters active on a visual cell (axes, legends, slicers, filters): each cell has its own.

Simple example: Zineb states: "the Lyon × January cell = sales filtered on Lyon and January".

What it is not: a single calculation valid for the whole visual.

Reflex: strange result → read the context out loud.

CALCULATE
Evaluates an expression in a modified context: it adds or overrides a filter.

Simple example: Zineb forces a filter on a city thanks to CALCULATE to isolate her result.

What it is not: a measure that undergoes the context without modifying it.

Reflex: modify a filter → CALCULATE.

ALL
Removes filters: ALL(Sales[City]) ignores the city filter — the key to "% of total".

Simple example: Zineb calculates the sales for all cities to compare each city with the total.

What it is not: a filter that stays active and skews the share.

Reflex: % of total → ALL on the column.

VAR / RETURN
DAX variables: you name an intermediate calculation once, then return it with RETURN.

Simple example: Zineb writes VAR Total = ... RETURN DIVIDE([Total Sales]; Total).

What it is not: the same calculation repeated three times in a formula.

Reflex: more than one line → VAR/RETURN.

Context transition
The advanced mechanism by which CALCULATE transforms a row context into a filter context.

Simple example: Zineb knows how to name it and plans to dig into it with the SQLBI reference.

What it is not: the simple filter context of a visual cell.

Reflex: to know by name, to explore later.

In practice — In practice — The "% of total" dissected

  1. Create a City × [Total Sales] matrix and READ each cell by stating its context: "this cell = sales filtered on Lyon", like Zineb.
  2. Write Sales All cities = CALCULATE([Total Sales]; ALL(Sales[City])) and add it to the matrix: the same value everywhere (the city filter is removed).
  3. Assemble with variables: Share city = VAR Total = CALCULATE([Total Sales]; ALL(Sales[City])) RETURN DIVIDE([Total Sales]; Total), then format in %.
  4. Test with an active Salesperson slicer: the share recalculates WITHIN the slicer's scope — reread the formula until you can explain why.
The trio context + CALCULATE/ALL + VAR mastered on the most requested calculation: the % of total.

Step-by-step script — read a context like Zineb (8 min)

1) Display [Total Sales] in a matrix, City in rows and Month in columns. 2) Click a cell and state its context out loud: the crossing of the two active filters. 3) Create Sales All cities = CALCULATE([Total Sales], ALL(Sales[City])): verify that the value is the same on every row. 4) Create Share city with VAR/RETURN: VAR Total = CALCULATE([Total Sales], ALL(Sales[City])) RETURN DIVIDE([Total Sales], Total), formatted in %. 5) Place a Salesperson slicer: observe the share recalculating within the scope, and explain it out loud.

If a total looks wrong, go back to step 2: nine times out of ten, the context explains the result.

Copy-paste template — my context measures

Revenue All Cities = CALCULATE([Total Revenue], ALL(Sales[City]))

City Share =
VAR Total = CALCULATE([Total Revenue], ALL(Sales[City]))
RETURN DIVIDE([Total Revenue], Total)   // %

Rule: read the context → modify it (CALCULATE/ALL) → structure it (VAR/RETURN).

Share of total

Zineb displays each city's share of the overall sales: the combo CALCULATE + ALL + VAR, formatted in %.

Total for all cities

Zineb isolates the grand total with ALL(Sales[City]): the same value repeated on every row, a constant reference.

Diagnosing a cell

A doubtful result: Zineb reads the context out loud before touching the measure — the bug is often there.

A readable formula

A five-line measure becomes clear thanks to VAR: the calculation named once, RETURN at the end.

✓ Best practices

  • Read a cell's context out loud to understand a result.
  • Use VAR/RETURN systematically as soon as a formula is longer than one line.
  • Start every diagnosis with the filter context.

✗ Common pitfalls

  • Writing unreadable nested CALCULATEs with no variables.
  • Confusing ALL (removes everything) and ALLSELECTED (respects slicers).
  • Looking for a bug anywhere but in the context.

Key takeaways

  • Every visual cell has its filter context: read it out loud to understand a result.
  • CALCULATE adds/overrides filters; ALL removes them: the two gestures cover most needs.
  • VAR/RETURN systematic as soon as a formula is longer than one line: readability AND performance.
  • A "strange" DAX result = a misunderstood context: the diagnosis always starts there.

48h challenge — apply it tomorrow

Reproduce Zineb's matrix: [Total Sales] by City, then add CALCULATE([Total Sales]; ALL(Sales[City])) and the Share city measure built with VAR/RETURN. Write in one sentence why the share recalculates when a slicer is set.

Section exit checklist
  • I can read a visual cell's filter context.
  • I can write the % of total with CALCULATE + ALL + VAR.
  • I diagnose a DAX result by starting from the context.
Self-assessment 1-5: do I explain the filter context without jargon?Self-assessment 1-5: did I build the % of total with VAR/RETURN?

Frequently asked questions

ALL, ALLSELECTED, ALLEXCEPT: how do I find my way?

ALL removes any filter on the target (the grand total); ALLSELECTED respects external selections, such as slicers; ALLEXCEPT removes everything except the columns cited. Start with ALL, move to ALLSELECTED when the % must follow the slicers, and keep ALLEXCEPT for later — the fine cases become clearer with practice and the SQLBI reference.

Why is my measure right in the detail but wrong on the Total row?

Because the Total row has a wider context: no city filter is active there. A poorly built measure degrades there — the classic case is the average of ratios. The usual solution is to recalculate by iteration with SUMX rather than averaging an already aggregated result. Reflex: right in the detail, wrong in the total → suspect a misplaced ratio.

Check your understanding

What is ALL for in CALCULATE?

Additional resources (optional, to go further)

Level 3 examFresh, harder questions to test what you've learned