skip to content

Data Modeling

Star schema, relationships with a cardinality and a filter direction, and the calculated-column versus measure choice. Interviewers ask about bidirectional filtering and many-to-many, because that is where models quietly start returning wrong numbers.

on this pageshow

questions

6

In Power BI, when should a calculation be a calculated column instead of a DAX measure?

level: juniorimportance: must knowfreq 85%

answer

  1. one is stored, one is not
  2. ask when the value gets computed
  3. slicers need values that already exist
  4. refresh time versus query time
  5. a stored percentage cannot be summed

basics

~20 s

Use a calculated column when the value must physically exist per row — for slicers, axes, grouping or relationship keys. Use a measure for anything aggregated, because measures are computed at query time and store nothing in the model.

solid answer

~50 s

A **calculated column** is evaluated once per row when the model refreshes and is stored in the table, so it costs memory and refresh time but can be used anywhere a real column can: on a slicer, on an axis, as a grouping field, or as a relationship key. A **measure** is not stored at all — it is evaluated at query time against whatever the visual is filtering, so it re-computes correctly at every level, including totals. The rule of thumb: if the value describes a row (a price band, a flag, a concatenated key), it is a column; if it aggregates rows (sales, margin %, distinct customers), it is a measure. Writing `Margin % = SUM(Sales[Profit]) / SUM(Sales[Revenue])` as a column is the classic mistake — a stored per-row ratio cannot be summed back up. Where possible, push column logic upstream into Power Query or the source rather than materialising it in DAX.

code

dax · 7 lines
dax
-- calculated column: one stored value per product row
Product[Price Band] =
    IF ( Product[ListPrice] > 100, "High", "Low" )

-- measure: no stored value, re-evaluated per visual cell
Margin % =
    DIVIDE ( SUM ( Sales[Profit] ), SUM ( Sales[Revenue] ) )

go deeper

for a junior

Be ready to state the rule cleanly: per-row value that users slice by means a column, aggregation means a measure. Know that a calculated column is stored and a measure is not.

for a middle

Explain the mechanics: calculated columns evaluate in row context at refresh and are compressed into VertiPaq; measures evaluate at query time under the visual's filters, which is why they give correct totals.

for a senior

Show the cost judgment. Talk about column cardinality driving model size, pushing column logic upstream to Power Query or the source, and auditing a bloated model for calculated columns that duplicate dimension attributes.

for a principal

Own the standard: where calculation logic lives across the stack, so the same business rule is not implemented three times in SQL, in M and in DAX, and so self-service authors are not materialising fact-table attributes that a star schema already provides.

## Two different objects that both use DAX A Power BI model (the semantic model, called a *dataset* before the Fabric-era rename) holds its tables in VertiPaq, an in-memory columnar engine. DAX can add two very different things to that model, and confusing them is the single most common beginner error in Power BI modelling. A **calculated column** adds a new column to an existing table. It is evaluated **row by row, once, at data-refresh time**, and the result is compressed and stored alongside every other column in the table. It has *row context*: inside the formula, a bare column reference means "this row's value", and `RELATED` can reach across a relationship to fetch the matching row on the one side. A **measure** adds a calculation to the model that has **no stored value at all**. It is evaluated **at query time**, once per cell the visual asks for, against the set of filters that cell is under. A measure has no row context of its own — it must aggregate (`SUM`, `COUNTROWS`, `SUMX`, …), which is why a measure body that references a bare column without an aggregator is an error. ## What that difference implies **Where you can use it.** A slicer, a chart axis, a matrix row header, a legend, a `Group by` field, and the key of a relationship all require *a column* — a real set of values that exists before any query runs. A measure cannot fill any of those roles; it only ever produces a value inside a cell. So if the requirement is "let users slice by price band", the price band must be a calculated column (or, better, a column created upstream). **Cost.** A calculated column consumes model memory and lengthens refresh. The cost is driven by *cardinality*: a low-cardinality column such as a three-value price band compresses to almost nothing, while a per-row calculated key or a per-row timestamp calculation can be one of the largest columns in the model. Calculated columns are also computed after the table is loaded, so they can compress worse than an equivalent column imported from the source. Measures cost nothing at rest; they cost CPU at query time. **Correctness at totals.** This is the deciding argument for ratios. If `Margin %` is stored per row, a matrix total can only aggregate those stored numbers — summing percentages is meaningless, and averaging them weights a one-dollar order the same as a million-dollar order. As a measure, `DIVIDE(SUM(Sales[Profit]), SUM(Sales[Revenue]))` re-evaluates at every grain, including the total row, and produces the correct weighted figure everywhere. ## The upstream option A third answer beats both in many interviews: do it in **Power Query** or in the source view. A column produced upstream is materialised the same way as any imported column, often compresses better, keeps DAX in the model simple, and (when the source is a database) can fold into SQL. Reserve calculated columns for logic that genuinely depends on the model — for example a value that needs `RELATED` across a relationship that only exists in Power BI, or a column derived from another calculated column. ## Worked contrast Given a `Sales` table with `Revenue` and `Profit`, and a `Product` table joined one-to-many to it: - `Product[Price Band] = IF(Product[ListPrice] > 100, "High", "Low")` — a calculated column. It is an attribute of a product, it has three possible values, it belongs on a slicer. - `Sales[Product Category] = RELATED(Product[Category])` — a calculated column that *works* but is usually a waste: the relationship already lets any visual group `Sales` measures by `Product[Category]`. Copying the attribute onto the fact table just spends memory and defeats the star schema. - `Total Profit = SUM(Sales[Profit])` and `Margin % = DIVIDE([Total Profit], SUM(Sales[Revenue]))` — measures. They must respond to whatever the user filtered. ## What interviewers listen for A strong answer names three axes — *when it is evaluated*, *whether it is stored*, and *where it can be used* — and then states the ratio-at-totals failure as the concrete consequence. A weaker answer describes them as two syntaxes for the same thing, or reaches for a calculated column first because it feels more like a spreadsheet. Mentioning that most calculated columns should have been created upstream is what separates a modeller from someone who only writes formulas.

  • Why can't you fix a wrong total by writing the ratio as a calculated column?
    Because a stored per-row ratio is just a number in a column, and the total row can only aggregate those numbers. Summing percentages is meaningless and averaging them weights every row equally. The correct total needs the ratio recomputed from summed numerator and denominator at the total grain, which only a query-time measure does.
  • When is a calculated column the wrong choice even though the value is per-row?
    When the source or Power Query can produce it. An upstream column usually compresses better, refreshes faster, keeps model DAX small, and can fold to SQL against a database. Reserve DAX calculated columns for logic that depends on the model itself, such as a value fetched with RELATED across a relationship that exists only in Power BI.
  • What does high cardinality do to the cost of a calculated column?
    VertiPaq compresses columns by their distinct values, so a three-value flag is nearly free while a per-row key, timestamp or computed decimal can become one of the largest objects in the model. Before adding a high-cardinality calculated column, check whether a measure or an upstream, lower-cardinality column would do.

A calculated column is like adding a column to a spreadsheet and filling it down once; a measure is like a formula in a summary cell that recalculates for whatever range you are currently looking at.

saying these in an interview costs you the question

  • Treats calculated columns and measures as interchangeable syntax
  • Stores a ratio like margin percent as a calculated column
  • Ignores memory and refresh cost of high-cardinality calculated columns
  • Thinks a measure can be dropped onto a slicer
  • Never considers building the column upstream in Power Query

context

open as a page

In Power BI, what do a relationship's cardinality and cross-filter direction control?

level: middleimportance: must knowfreq 75%

basics

~20 s

Cardinality declares which side of the relationship holds unique key values; cross-filter direction declares which way a filter travels along it. A one-to-many relationship filters from the one side down to the many side, and by default not back.

open as a page

In Power BI, why does a matrix show a (Blank) row for a dimension attribute?

level: middleimportance: should knowfreq 50%

basics

~20 s

Power BI adds a hidden blank row to the one side of a relationship when the fact table contains key values that have no match, or nulls. Those fact rows are grouped under (Blank) so the total still includes them.

open as a page

In Power BI, what goes wrong when you set a relationship's cross-filter direction to Both?

level: seniorimportance: should knowfreq 65%

basics

~20 s

Bidirectional filtering lets filters travel back up from the fact table, which can create more than one route between tables, make results depend on the path taken, slow queries, and interact badly with row-level security. Scope it per measure instead.

open as a page

In Power BI, when should you use a bridge table instead of a many-to-many cardinality relationship?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Use a bridge table whenever you need strong relationships, a slicer over the shared keys, or reliable totals. Power BI's many-to-many cardinality creates a limited relationship: no blank row for unmatched keys, no RELATED, and totals that can stop matching the visible rows.

open as a page

In Power BI, how do you model both order date and ship date against one Date table?

level: middleimportance: nice to knowfreq 42%

basics

~20 s

Only one relationship between two tables can be active, so the second date relationship is created inactive. Either activate it per measure with USERELATIONSHIP, or load a second, separately named date table so users can slice by each role directly.

open as a page