DAX Fundamentals
Master core DAX concepts to create calculated columns and measures that power insights.
Content
Calculated columns vs measures
Versions:
Watch & Learn
AI-discovered learning video
DAX Fundamentals: Calculated Columns vs Measures — Choose Your Fighter
You have got a clean semantic model (chef’s kiss), relationships that are not chaos (we survived many-to-many), and your MonthName now knows how to behave thanks to Sort by Column. Today’s quest: the psychic damage of mixing up calculated columns and measures.
If relationships are the skeleton of your model, DAX is the nervous system — and columns vs measures are the difference between bones and brain signals.
Why this matters: pick the wrong one and your report either bloats like a memory-hoarding dragon or answers the wrong question faster than you can say “refresh failed.”
The 30-Second TL;DR (but stay for the plot twist)
- Calculated columns: Computed row-by-row during data refresh; stored in the model; great for attributes, keys, and slicers. Not responsive to slicers/filters after refresh.
- Measures: Calculated on the fly based on the current filter context; not stored; great for aggregations, KPIs, and time intelligence. Fully responsive to slicers/filters.
Think: columns are pre-baked; measures are cooked-to-order.
The Showdown: Side-by-Side
| Feature | Calculated Column | Measure |
|---|---|---|
| When it’s calculated | During data refresh (Import) | At query time, with current filters |
| Storage impact | Takes model memory | Little to none (just the expression) |
| Use in visuals | Can be used as categories, slicers, group-by, relationships | Values in visuals (aggregations/KPIs), not categories or relationships |
| Responds to slicers | No (values are static post-refresh) | Yes (re-evaluates with context) |
| Typical use | Sort keys, bucketing, business logic attributes, keys for joins | Totals, averages, ratios, YTD, rolling windows |
| Context used by default | Row context | Filter context |
Columns define what the world looks like. Measures tell you what’s happening in that world right now.
Two Brain Modes: Row Context vs Filter Context
You met relationships earlier; now meet their besties:
- Row context: DAX is looking at one row at a time. This is column country.
- Filter context: DAX is evaluating with a set of filters (from slicers, visuals, relationships). This is measure nation.
And then there’s the plot device: CALCULATE — the function that changes filter context and makes measures do parkour.
Real-World Mini-Scenarios
1) The Line Amount Debate
Option A: Precompute it as a column (faster aggregations, more RAM).
-- Calculated Column in Sales table
Sales[LineAmount] = Sales[Quantity] * Sales[Unit Price]
Then use a measure to total it:
Total Sales = SUM(Sales[LineAmount])
Option B: Go full measure, save memory (more CPU at query time):
Total Sales (No Column) = SUMX(Sales, Sales[Quantity] * Sales[Unit Price])
Which to choose? If your Sales table is enormous and memory is tight, prefer the measure-only version. If you want snappy aggregations and have room, a LineAmount column plus a simple SUM is classic and fast.
2) Profit Margin (Please don’t column this)
Bad idea: a Profit Margin column per row — it’s static and misleading when filters change. Good idea: a measure.
Total Cost = SUM(Sales[Cost])
Total Sales = SUM(Sales[LineAmount])
Profit Margin % = DIVIDE([Total Sales] - [Total Cost], [Total Sales])
Now Margin responds to product, region, date, and any filter combo like a polite guest.
3) Time Intelligence That Actually Intel-ligences
You learned to create a proper Date table (right? right). Use measures for YTD/MTD:
Sales YTD = CALCULATE([Total Sales], DATESYTD('Date'[Date]))
Do not try to make a YTD column unless you enjoy sadness. Time intelligence thrives on filter context.
4) Sort by Column Throwback
From earlier: MonthName sorted by MonthIndex. That MonthIndex? Definitely a calculated column in the Date table.
Date[MonthIndex] = YEAR(Date[Date]) * 100 + MONTH(Date[Date])
Then Model view: Sort MonthName by MonthIndex. Chef’s kiss charts.
5) Many-to-Many and the Bridge
We built bridges to tame many-to-many. Columns help define keys and relationships (e.g., concatenating codes). Measures are where the math happens across the bridge. Example: count of active subscriptions by a set of tags via a tag bridge. The measure respects filter propagation and won’t double-count if your model is clean.
Decision Tree (a.k.a. sanity check)
Ask this before writing DAX:
- Do I need this value to change with slicers or context?
- Yes → It’s a measure.
- No → Maybe a column.
- Do I need this value as a category, slicer, or relationship key?
- Yes → Column.
- No → Could be a measure.
- Is this an aggregation, KPI, or ratio across many rows?
- Yes → Measure.
- Is this row-level metadata (bucket, label, sort key, join key)?
- Yes → Column.
- Performance/memory trade-off?
- Huge table, high cardinality calc → Prefer measure (no extra storage).
- Need blazing-fast simple sums → Precompute column + SUM.
Gotchas People Keep Tripping Over
- You cannot put a measure in a slicer or use it to sort a column. Measures are not categories.
- Calculated columns don’t react to slicers. If your metric should move with filters, do not calc-column it.
- DirectQuery nuance: calculated columns may still be computed by the engine and can be limited; use them sparingly and prefer pushing logic to the source when possible.
- Row context does not automatically aggregate. Writing a column like
Sales[% of Total]won’t work as you think — it has no idea what “total” means per filter. That’s a measure job. - Measures live their best life in a star schema (which we built). In a spaghetti schema, even the best measure cries.
Quick Build: From Model to Insight
Let’s make something you would actually ship.
- Prep your row-level logic (columns):
-- In Sales
Sales[LineAmount] = Sales[Quantity] * Sales[Unit Price]
-- In Date (you already have a proper Date table)
Date[MonthIndex] = YEAR(Date[Date]) * 100 + MONTH(Date[Date])
Model: Sort Date[MonthName] by Date[MonthIndex].
- Define your measures (responsive KPIs):
Total Sales = SUM(Sales[LineAmount])
Total Cost = SUM(Sales[Cost])
Gross Profit = [Total Sales] - [Total Cost]
Profit Margin % = DIVIDE([Gross Profit], [Total Sales])
Sales YTD = CALCULATE([Total Sales], DATESYTD('Date'[Date]))
- Visuals:
- Axis: Date[MonthName] (sorted by MonthIndex)
- Values: [Total Sales], [Profit Margin %], [Sales YTD]
- Slicers: Product[Category], Geography[Region]
Everything dances when you filter. That is the measure effect.
Performance Notes You’ll Thank Me For Later
- Memory vs CPU: columns cost memory; measures cost CPU at query time. Decide based on model size and expected interactivity.
- High-cardinality columns (e.g., unique IDs) are already large — avoid adding more big calculated columns unless you must (like relationship keys).
- Precomputing repeated math at the row level (LineAmount) can speed up visuals dramatically, especially for heavy use.
- Keep measures simple and composable. Build [Gross Profit] from [Total Sales] and [Total Cost]. Future you will cry fewer tears.
Pro tip: a small, tidy model with smart measures often beats a huge, pre-baked model that tries to do everything in columns.
Why People Misunderstand This (and how you won’t)
Because both use DAX and both show up in the Fields pane. But they have different jobs:
- Columns are part of the data itself — attributes you can slice by.
- Measures are answers to questions — values that morph with context.
Imagine a menu: columns are the ingredients listed on the back; measures are the dish the chef makes when you order “medium spicy, no cilantro, extra lime.”
Wrap-Up: Put It All Together
- Use calculated columns for: sort keys, bucketing (e.g., AgeGroup), relationship keys (e.g., composite surrogate keys), labels you want to slice by.
- Use measures for: totals, averages, ratios, time intelligence, and anything that should change with slicers.
- Marry this with your star schema and relationship directions from earlier modules to keep results trustworthy.
Key insight:
Columns define the stage; relationships block the actors; measures deliver the lines at showtime.
Your next move: audit your current model. For every piece of DAX, ask: does this need to react to filters? If yes, promote it to a measure. If no, consider a column — but watch the memory bill. Congratulations, you now wield the two forces that make Power BI go brrr in the most responsible way possible.
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to comment!