clement13.net
Original photo: Touches numérotées d'une calculatrice mécanique de bureau, sous son capot transparent.
Description
Touches numérotées d'une calculatrice mécanique de bureau, sous son capot transparent.
Source
stiefkind · original page
Licence
CC0 Public domain
Original photo, unretouched. The background is a version of it stylised by the site.

DAX Measures and Calculations

This theme assesses the quality of calculation code: measures rather than columns, command of filter context and row context, context transition (CALCULATE), variables, time…

Guide≈ 7 min readCheckedCheck doneFacts, figures and references were checked against the sources cited in the page.Written with AI assistance; sources are cited at the bottom of the page. An error? Report it via clement13.net.Reading
In brief

This theme assesses the quality of calculation code: measures rather than columns, command of filter context and row context, context transition (CALCULATE), variables, time intelligence, reusable base measures, calculation groups, naming and formatting conventions, and identifier stability (lineage) to avoid breaking client reports. Tests for these measures are in TST.

On this page

Level: 🟢→🔴 · Theme code: DAX

1. Why this theme#

Measures are the business logic of the report. A rule (“visit completed,” “active customer,” “numeric distribution”) copied into twenty measures will drift. DAX has two engines (formula and storage) and two contexts; classic mistakes (iterating an entire table in FILTER, forgetting context transition) produce numbers that are wrong or slow with no visible error. Cost of ignoring it: contradictory numbers across pages, fear of touching measures, and client reports broken by renaming.

2. What the theme measures#

  • Axis: from improvised calculated columns to a readable, tested, versioned, and stable measure library.
  • Key indicators: share of measures built on base measures; number of business rules defined in multiple places; share of documented measures; number of renamed/deleted measures that broke a report.
  • Units: %, number.

3. The 5 levels#

Level Short name Promise Typical indicator (orders of magnitude)
1 Columns and copy-paste The expected number appears for the planned view, and only that one. Calculated columns everywhere; implicit measures
2 Named measures Every displayed number comes from an explicit, named, and formatted measure. 0 implicit measures in reports
3 Controlled A number keeps the same meaning regardless of visual, filter, or period. Base measures + variables; correct time intelligence
4 Reusable A business rule exists only once, and a new variation is added in minutes. 1 rule = 1 definition; calculation groups
5 Library Calculation is a product: tested, versioned, documented, and changes do not break any consumer. 0 reports broken by measure changes in 12 months

4. Milestones by level#

ID Milestone Verifiable evidence
BI-DAX-1.1 Main numbers are calculated in the model (not manually in Excel) Value can be recalculated
BI-DAX-1.2 Results are compared once to the source Recorded spot check
BI-DAX-1.3 Calculated columns are known and inventoried List of calculated columns
BI-DAX-2.1 No “implicit aggregation”: numeric columns are set to Do not summarize Summarization = none on columns
BI-DAX-2.2 Measures named by convention (Verb/Noun + optional unit) and formatted Naming review
BI-DAX-2.3 Measures organized in display folders Folder structure
BI-DAX-2.4 No unprotected division (DIVIDE) Review of / in code
BI-DAX-3.1 Base measures (sum, count) are separate from derived measures Measure dependency tree
BI-DAX-3.2 Use of VAR/RETURN for readability and performance Code review
BI-DAX-3.3 Time intelligence on a marked date table (DATESYTD, SAMEPERIODLASTYEAR, DATEADD) Results verified on leap years and incomplete years
BI-DAX-3.4 CALCULATE filters expressed as column predicates, not as FILTER(TABLE…) unless needed Performance review
BI-DAX-3.5 Context transition is understood and documented where it appears (measure inside an iterator) Comments and test cases
BI-DAX-4.1 Each business rule (“visit completed”) defined once, reused by reference No duplication detected
BI-DAX-4.2 Calculation groups for cross-cutting variations (period, currency, variance) Calculation table in the model
BI-DAX-4.3 Systematic variation nomenclature (base / cumulative / N-1 / %) Suffix table
BI-DAX-4.4 Commented measures: intent, assumptions, exceptions Measure description filled in
BI-DAX-4.5 Hidden (internal) measures separated from published measures Visibility managed
BI-DAX-5.1 Measures are tested (reference queries) and versioned (TMDL) Repository + tests (TST)
BI-DAX-5.2 A published measure is never deleted/recreated; it is modified in place (lineageTag preserved) Repository history
BI-DAX-5.3 Deprecation policy (marking, timeline, communication) for retired measures Deprecation log
BI-DAX-5.4 Reusable measure library across models (e.g., TMDL scripting tool) Shared repository

5. Detailed reading by level#

🟢 Level 1 — Columns and copy-paste#

  • Signs: calculated columns for everything (=[Quantity]*[Price]), implicit measures created by drag-and-drop, values that vary by visual.
  • What is understood at this stage: a calculated column is evaluated at load time, stored in memory, in a row context; a measure is evaluated at display time, in the visual's filter context. Columns consume memory and do not react to filters.
  • Difficulty: 1/5 · Effort: 1/5.

🟢 Level 2 — Named measures#

Nb Visites = COUNTROWS ( 'FACT_VISITE' )

Taux Réalisation =
DIVIDE ( [Nb Visites Réalisées], [Nb Visites Planifiées] )
  • Signs: implicit aggregation disabled, meaningful names, formats set, systematic DIVIDE.
  • Benefit: readability, display consistency.
  • Limit: rules are still duplicated; time intelligence is often approximate.
  • Difficulty: 2/5 · Effort: 2/5.

🟡 Level 3 — Controlled#

Understanding context:

-- Mesures de base
Nb Visites Réalisées =
CALCULATE ( [Nb Visites], 'FACT_VISITE'[Statut] = "Réalisée" )

-- Time intelligence : exige une table de dates continue et marquée
Nb Visites N-1 =
CALCULATE ( [Nb Visites Réalisées], SAMEPERIODLASTYEAR ( 'DIM_DATE'[Date] ) )

Évolution N-1 % =
VAR vCourant = [Nb Visites Réalisées]
VAR vPrec    = [Nb Visites N-1]
RETURN DIVIDE ( vCourant - vPrec, vPrec )
  • Context transition: a measure called inside an iterator (SUMX) carries an implicit CALCULATE that converts row context into filter context. A source of errors and cost: SUMX(Clients, [Mesure]) evaluates the measure as many times as there are customers.
  • Predicate filter: CALCULATE(..., 'T'[Col] = x) is folded into a column filter (efficient); FILTER('T', …) iterates the table (costly).
  • Benefit: same numbers regardless of visual.
  • Limit: each new variation still requires code.
  • Difficulty: 3/5 · Effort: 3/5.
⚠️ Pitfall

Symptom: “N-1” is blank or wrong at the start of a period. Cause: date table that does not cover the previous year, or is not marked. Fix: continuous calendar across the full range + horizon; test boundaries (January 1, February 29).

⚠️ Pitfall

Symptom: a “Total” measure smaller than the sum of rows. Cause: non-additive calculation (averages, distinct counts) or lost total filter. Fix: explicitly define total behavior (HASONEVALUE, ISINSCOPE) and test it.

🟡 Level 4 — Reusable#

  • Signs: one business rule = one base measure. Variations (YTD, N-1, % of) go through calculation groups:
-- Élément de calcul « Période » : N-1
CALCULATE ( SELECTEDMEASURE (), SAMEPERIODLASTYEAR ( 'DIM_DATE'[Date] ) )

A calculation group replaces dozens of duplicated measures, but requires precautions: precedence between groups, SELECTEDMEASURENAME() to handle percentages differently, dynamic formats, and verification that already published measures keep their results.

  • Benefit: consistency, adding a variation in minutes.
  • Limit: hard to debug without a method; existing client reports must remain compatible.
  • Difficulty: 4/5 · Effort: 4/5.
🧪 Example

SFA thread: the rule “active customer = at least one completed visit over rolling 90 days” is defined in [Clients Actifs]; all “by active customer” measures reference it. When the rule moves to 120 days, you modify one measure.

🔴 Level 5 — Library#

  • Signs: measures are in a repository (TMDL), tested with reference queries, documented, and never deleted-recreated: renaming or deleting a measure loses its lineageTag and breaks linked reports. An obsolete measure is marked, moved, communicated, and removed after a delay.
  • Benefit: calculation is a stable product; change cost is predictable.
  • Limit: review discipline is essential.
  • Difficulty: 5/5 · Effort: 5/5.
-- Test de mesure (DAX query, exécutable dans DAX Studio ou SSMS)
EVALUATE
ROW (
    "Attendu", 12345,
    "Calculé", [Nb Visites Réalisées],
    "Écart",   [Nb Visites Réalisées] - 12345
)
✅ Good practice

Write DAX to be read, not just executed: VARs named vXxx, one CALCULATE per intent, and // comments for exceptions. Format with a dedicated tool (DAX Formatter) and keep formatting in the repository.

💡 Expert tip

DEFINE MEASURE in a DAX query lets you prototype a measure without touching the model, compare two variants, and measure timings (Server Timings) before integrating them.

🤖 AI

Assistants write common measures well and explain a CALCULATE; they make mistakes on contexts (plausible output, wrong result), on inactive relationships, and on totals. Always validate with a test query against a known expected value.

6. Partial achievement and rating#

Common rule. Each DAX milestone is assessed by sample: draw 10 random measures and count those that meet the milestone (≥ 8/10 → 1; 5 to 7 → 0.5; < 5 → 0). “A partial 5”: tests cover the 30 most used measures out of 220 (BI-DAX-5.1 = 0.5), the “never delete-recreate” rule has been followed for 6 months (5.2 = 1), no deprecation policy (5.3 = 0).

7. Moving to the next level#

Possible means (optional):

Transition Possible levers
1 → 2 Disable implicit aggregation; naming convention; DIVIDE
2 → 3 Base measures; variables; marked calendar; plan reading (DAX Studio)
3 → 4 Rule inventory; calculation groups; variation nomenclature
4 → 5 TMDL repository, reference tests, deprecation policy, generation scripts

Pitfalls:

  • Goodhart: “100% of measures documented” → meaningless descriptions (“Count measure”).
  • Rewriting all measures “to clean things up”: you lose lineageTag, you break client reports.
  • Calculation group enabled too early: side effects on all model measures, including those not designed for it.
  • False positive: a fast and wrong measure; speed is not correctness.

8. See also#

✻ Written by Claude Sonnet 5.5