Two languages, two engines: M prepares the data once, DAX calculates on it with every click. From your first SUM to CALCULATE, the window function family, and twenty advanced DAX functions.
The hands-on exercise at the end of the lesson. It's worth opening Power BI Desktop and the AdventureWorks file right now — with the model you built in lesson 2 and the cleanup from lesson 3.
practice
AdventureWorks_Sales.xlsx
121,253 sales rows, 397 products, and 18,485 customer keys — now we compute on them.
Before writing a single formula — you understand who prepares the data and who analyzes it. That's the split that explains the whole tool.
The data sourceExcel · SQL · API
←
Power QueryThe M Language — cleaning and prep
←
The data modelVertiPaq in memory
←
DAXMeasures and calculations
←
visualThe number on the screen
M
Builds the infrastructure
Runs once on refresh, cleans and shapes, and then the table is "baked in" and doesn't change until the next refresh.
DAX
analyzes it
Reruns on every user click, responds to every slicer, and returns a live number.
the moral: M sets the table — once, before the guests arrive. DAX cooks to order — fresh every time.
Chapter I · Two languages, two engines
The M language — the five roles
M It's a functional scripting language that runs behind the scenes of Power Query. Every click in the graphical interface translates into a step written in M — you're writing M even without knowing it.
01
Connecting to sources
Excel, files, databases like SQL Server and-Oracle, API and websites — a single entry point for all the data.
02
Data cleaning
Removing empty and duplicate rows, handling errors, replacing values and changing data types — for example converting text to a date.
03
Reformatting
Structural operations: splitting columns, grouping Group By, and turning rows into columns and vice versa Pivot / Unpivot.
04
Combining tables
Merge — merge by key, or Append — one appended below the other. From different sources into one clean table.
05
Automation
The process is saved as a fixed set of steps. Click refresh — and all the cleanup steps rerun on the updated data.
The key point: M works at the preparation stage — before the data is displayed or analyzed. Write it once ← runs on every refresh, forever.
Chapter I · Two languages, two engines
M syntax — let, in, and case sensitivity
Every query in M is built from a block let … in: a list of steps, each relying on the previous one, and at the end the step to return as the result.
Only during the refresh. Afterward the work is finished and the table is fixed
Case sensitivity
yes — Table.AddColumn works, table.addcolumn will throw an error
structure
let to the steps · in to output. Every step gets a name and relies on the previous one
A familiar parallel
SELECT · JOIN · WHERE in SQL, or Pandas in the Python world
✓ Rule of thumb: "do you need to change the table's structure or clean data?" ← that's M's job.
Chapter I · Two languages, two engines
The DAX language — the five roles
DAX — Data Analysis Expressions — is the formula language of the data model. The syntax resembles Excel, but Excel operates on individual cells while DAX operates on entire columns and tables.
01
Measures (Measures)
The real power: dynamic calculations that change in real time based on what the user selects. Total revenue, average per customer, profit percentage.
02
Calculated columns
New data for an existing table, calculated per row — for example unit price × quantity = total sale per row.
03
Calculated tables
brand-new tables built from the existing data — a dates table, a summary table, a parameters table.
04
Time Intelligence
Comparisons over time: YTD, this month vs. the same month last year, moving averages.
05
Controlling the Filter Context
With CALCULATE you change the filtering rules: showing sales of a specific product even when the chart displays all of them.
The key point: DAX kicks in after M — it doesn't clean or load, it analyzes. Live numbers that respond to every click.
Chapter I · Two languages, two engines
M vs. DAX — the comparison table
The same file, two completely different engines. This table is the whole difference.
property
The M Language · Power Query
The DAX Language · Data Model
Main goal
Data shaping, cleaning, and preparation (ETL)
Analysis, aggregations, and business calculations
When it's calculated
Only during a refresh (Data Refresh)
dynamically, while using the dashboard
Which engine
Power Query Mashup Engine
VertiPaq — In-Memory Columnar
Case sensitivity
Yes (Case Sensitive)
No (Case Insensitive)
rule of thumb
"Change structure or clean up data?"
"Calculate something that responds to filters?"
M Prepares the table — once. DAX Cooks to order — fresh every time.
Chapter I · Two languages, two engines
The golden rule — pushing the processing back
Roche's Maxim: Perform every transformation as far "back" in the chain as possible — as close as possible to the data source.
1
The database
SQL / Database. If the transformation can be done here — this is the preferred option. The server does the work, not the report.
2
Power Query
The M language. If it can't be done in the database — clean and shape it here, once per refresh.
3
DAX
Data Model. Only for calculations that must be dynamic and respond to the end user.
Order of priority: Data source ← Power Query ← DAX. The further back, the lighter and faster the model.
✓ The common mistake: building a calculated column in DAX when it could have been calculated in M. The column takes up memory on every refresh, forever.
Chapter II · Creating Tables in M
Three ways to create a table in Power Query
Before moving to DAX — create tables in the editor. Three ways, each for a different situation.
01
External source
Loading from Excel or from a database — like the seven tables of AdventureWorks.
02
Reference
Deriving a new table from an existing table. Changes in the original flow to it automatically.
03
Enter Data
Manual entry of a small table — a mapping table, a parameter table, a list of targets.
M
let Source = Sales, Cols = Table.SelectColumns( Source, {"CustomerKey"} ), Uniq = Table.Distinct( Cols ) in Uniq
note:Reference Continues from the original query, Duplicate Creates a disconnected copy. Every table created in M is \"baked in\" — it stays fixed until the next refresh.
Chapter II · Creating Tables in M
Demo — dimension table via Reference
Derive a dimension table from the fact table — five clicks, without writing a single line of code.
Right-click on Sales ← ReferenceA new query is created pointing to Sales as the starting point.
Choose ColumnsKeep only the CustomerKey and drop the rest.
Remove Rows ← Remove DuplicatesGet a list of distinct customers.
Rename ← DimCustomerThe query name is the table name in the model.
Close & ApplyThe table loads into the model and is ready to relate.
18,485Rows in the new table
18,484Real customers
-1The technical key of the Reseller channel
✓ A one-row difference: sales through the Reseller channel carry CustomerKey = -1 — \"not relevant\". Remember this, we'll come back to it with counts.
Chapter II · Creating Tables in M
Date table — the three conditions
This is the most common table analysts create in M — in almost every model you'll build in your career. Without it there's no YoY, no YTD, and no period comparisons.
1
continuous
Every day in the range, with no gaps. In our case: 1,461 consecutive days from July 1, 2017 to June 30, 2021.
2
Unique key
DateKey One per day — this is the 1 side in the relationship to the fact table.
3
marked
Mark as date table — without this, Time Intelligence functions won't work correctly.
step
What to click
1
Home ← Get Data ← Blank Query
2
in the video: Advanced Editor
3
Delete the content and paste the script
4
we make sure ✓ No syntax errors ← Done
5
Right click ← Rename ← Date Ultimate ← Close & Apply
6
In the model: relationship OrderDateKey ← DateKey + Mark as date table
The ready-made script: Date_Ultimate_Table.m — 34 columns, including AdventureWorks's fiscal year (July–June), matched 1:1 toOrderDateKey in the file.
Chapter II · Creating Tables in M
This is what it looks like on screen — step by step
Four screenshots from the editor — exactly what you will see when you do this yourself.
Step 1 — Open the editor tab Home ← Advanced EditorThe button sits in the Query group, to the left of Manage.Step 2 — an empty Query1 opens Delete all the content inside and paste the full script.Step 3 — check downward The message must appear ✓ No syntax errors have been detected Before clicking Done.Step 4 — right-click on the queryRename ← Date Ultimate. The query name is the table name that will appear in the model.
✓Don't forget:Close & Apply, and then on screen Model — building the relationship OrderDateKey ← DateKey and mark Mark as date table.
Chapter III · Intro to DAX
What is DAX — and why you can't do without it
The model is connected: 121,253 sales rows, products, customers and dates. The data sits there — but it does not say anything.
What do we have
Connected tables
Seven tables, valid relationships, a tidy star schema. But there's no column in the file named "profit" or "customer count".
What's asked
Business questions
"How much did we sell?" · "What's the profit?" · "Bikes only?" · "How many customers?" — there's no ready-made column for any of these questions.
Raw dataWhat gets loaded into the model
←
DAXThe formula language
←
Business answersLive numbers
"Alive" = recalculated all the time. the user clicked "Bikes"? All the measures are recalculated in that same instant, just for bikes.
Chapter III · Intro to DAX
Three things DAX knows how to create
three objects, and each has its own place. The difference between them is the most important difference in the lesson.
Calculated Table
A whole new table
When? When you need a table that does not exist in the source — a dates table, a parameter table. Relatively rare: most tables come from M.
Calculated Column
A value per row
When? When you need a per-row value for filtering or grouping — for example Profit for every sales row. Calculated on refresh, stored physically, takes up memory.
⭐ Measure
Live running calculation
When? Almost always — for every summary and calculation. Computed on every interaction, not stored. This is 90% of the lesson.
Three parts, always: measure name ← equals sign ← the formula.
DAX
Total Sales = SUM ( Sales[Sales Amount] )
Measure name · equals sign · function ( table[column] )
what you see
what is this
Sales[Sales Amount]
A column inside a table — the table name before the square brackets
[Total Sales]
Existing measure — square brackets without A table name before them
SUM ( … )
A function — a name, round parentheses, and inside them the arguments
✓The habit that prevents 100 bugs: See square brackets without a table name before them? That's always a measure — not a column.
Chapter IV · Aggregate Functions
SUM — the first measure
adds up all the values in a single numeric column — into one sum. This is the first measure every analyst writes.
DAX
Total Sales = SUM ( Sales[Sales Amount] )
$109,809,274The result on the fileTotal Sales
Accessing a columnSales[Sales Amount] In the Sales table.
Iterates over all 121,253 valuesAn empty cell (Blank) is simply skipped.
Returns a single numberThe sum — and a single number is exactly what a KPI visual needs.
A limitation worth knowing: SUM accepts Just one column, not an expression. Need a calculation within the row, like quantity × price? For that there's SUMX — in Chapter VI.
Chapter IV · Aggregate Functions
AVERAGE and MAX — the same family
Same engine, different ending: AVERAGE returns the average, MAX the highest value.
DAX
Avg Line = AVERAGE ( Sales[Sales Amount] ) Biggest Line = MAX ( Sales[Sales Amount] )
$905.62Average per sales rowAVERAGE
$27,893.62The largest sales rowMAX
function
What's important to know
AVERAGE
Sum ÷ number of cells non-empty. Blank isn't counted in the denominator — a point that surprises many
MAX
The highest value. It also works on dates — "the latest date"
MIN
The reverse twin, the exact same syntax
✓to read the result correctly: $905.62 is the average forSale row — not per customer and not per order. Always ask: "average of what?"
Chapter IV · Aggregate Functions
COUNTROWS — row counting
Counts how many rows are in the table — how many row-transactions passed through the system.
DAX
Total Lines = COUNTROWS ( Sales )
121,253Rows in the Sales tableCOUNTROWS
Note the syntax: COUNTROWS accepts table, not a column. It's the safest function for counting transactions — it doesn't depend on any specific column and isn't affected by blank cells.
✓ It's also the basis for advanced conditional counts: you feed into it FILTER( … ) and we get "how many rows meet the condition". We'll see this in Chapter VII.
Chapter IV · Aggregate Functions
DISTINCTCOUNT vs. COUNT — the critical difference
COUNT counts how many filled cells there are in a column. DISTINCTCOUNT counts how many different There is one. The difference is the difference between "how many rows" and "how many customers".
One customer who bought 25 timesCOUNT on CustomerKey will count it 25 times — once per row.
DISTINCTCOUNT filters firstRemoves duplicates and only then counts — the customer is counted once.
so"How many customers?" = always DISTINCTCOUNT.
✓A real trap from the file: The result is 18,485 — not 18,484. The Reseller channel's sales carry CustomerKey = -1, and -1 is counted as a customer just like any other. Always ask: "who's hiding in my keys?"
Count transactions ← COUNTROWS · to count distinct values ← DISTINCTCOUNT. COUNT on text has COUNTA.
Chapter IV · Aggregate Functions
DIVIDE — safe division
Divides a number by a number — without crashing when the denominator is zero.
11.4%The profit margin across the whole fileDIVIDE
argument
What it does
numerator
The numerator: a number, a measure, or an expression
denominator
The denominator: if it's 0 or Blank — the division doesn't happen
[alternate]
Optional: what to return when the denominator is zero. Omitted it? A silent Blank will be returned
A professional iron rule: In measures you never use a slash ( / ). One cell with a zero denominator — and the whole visual crashes with an error. Always DIVIDE.
Chapter IV · Aggregate Functions
Layered measures — a measure calling a measure
You build a library of measures like Lego: each new measure builds on the previous ones, and formulas aren't duplicated.
DAX
Total Sales = SUM ( Sales[Sales Amount] ) Total Cost = SUM ( Sales[Total Product Cost] ) Total Profit = [Total Sales] - [Total Cost] Margin % = DIVIDE ( [Total Profit], [Total Sales] )
$109,809,274Total Sales
$12,551,366Total Profit
11.4%Margin %
the advantage: Changed something in Total Sales? The whole chain updates on its own. One place to fix, instead of four scattered formulas.
✓ Another professional habit: create a table Measures empty (Enter Data) table and gather all the measures in it. They do not belong to any data table.
Chapter V · Calculated columns
Pattern 1 — Arithmetic Between Columns
A new column that calculates a value per row from other columns In the same row. Here you first encounter Row Context.
Where? Right-click the table name in the Data pane ← New Column.
The engine is at row 1Calculates revenue minus cost of that same row only.
moves to row 2, 3 … up to 121,253Each row its own value — that's Row Context.
The values are stored physicallyYou see them in Data View, like any other column.
$12,551,366SUM on the new columnSUM(Sales[Profit])
✓The difference from the measure: The exact same calculation — but here it runs once and is stored, while in a measure it reruns on every click. Column = raw material for slicing · measure = the aggregate answer.
Chapter V · Calculated columns
Pattern 2 — IF: classification by condition
A column that classifies each row by a condition — yes/no, bigger/smaller.
Any logical expression: comparison to a constant, column vs. column, and even combinations with && (and) or || (or)
then
The value when the condition holds
[else]
optional! If omitted, rows that didn't meet the condition get Blank
✓Another strong example: column Loss Flag that compares sale against cost reveals that21,829 rows sold at a loss. One column — a complete business insight.
Chapter V · Calculated columns
Pattern 3 — SWITCH: classification tiers
How many classification tiers? SWITCH replaces a ladder of IF-inside-IF — with clean, readable code.
The professional trick: instead of a value to check, you write TRUE() — and every "value" becomes a free logical condition
value ← result pairs
Checked from top to bottom. The first one that's true wins, and the rest aren't checked
[else]
A single value at the end, with no pair — the default for everything else
The waterfall rule: if >= 1000 appears first, it will also catch transactions of 20,000 — and "large" will never happen. Always from strictest to loosest, from high to low.
Chapter V · Calculated columns
Pattern 4 — RELATED: between tables
Pulls a value from another table into the row — through a relationship you've already built in the model. It's the VLOOKUP of DAX, without the pain.
DAX
// Sales ← New Column Category = RELATED ( Product[Category] )
Represents a sales rowReads its ProductKey.
Jumps through the *:1 relationshipto the Product table and locates the matching row.
Copies the categoryto the sale row — and so on for all 121,253 rows.
41,193Bikes rows
40,005Accessories rows
rule
explanation
direction
Works only from the * side to the 1 side — from the fact to the dimension. Every sales row has exactly one product
argument
receives A specific column, not a table name
requirement
needs a Row Context — meaning a calculated column or an iterator (SUMX and its siblings)
No relationship?
The function won't even be suggested in autocomplete
Chapter V · Calculated columns
Pattern 5 — text concatenation and bucketing
The last two patterns, and both are written in the dimension table — not the fact table.
✓the principle: A column in the dimension (397 rows) serves, through the relationship, all 121,253 sales. Classify it in the dimension, not the fact — less memory, the same result.
Chapter VI · Iterators
SUMX — Row-by-Row Calculation
Calculates an expression inside every row — and only then adds up the results. That's what SUM doesn't know how to do.
In each row: quantity × priceThe calculation runs within the context of the single row.
Combines the 121,253 resultsand returns a single number.
Why not SUM? because SUM only accepts a single column — it doesn't know how to multiply two columns. For multiplication within a row you need an iterator.
✓ The siblings of SUMX: AVERAGEX · MAXX · MINX · COUNTX — the exact same syntax, a different ending.
Chapter VI · Iterators
The gap revealed — $527,508 in hidden discounts
Two numbers, one story — and that is the difference between knowing functions and being an analyst.
Discount Given = [Before Discount] - [Total Sales]
$110,336,782Before discountSUMX
$109,809,274actually paidSUM
$527,508Total discountsthe difference
And the twist: column Unit Price Discount Pct Shows 0 in every row. But 3,282 rows actually got a discount — in tiers from 2% up to 40% — all on the Reseller channel (the -1 we already know). The data "lied" in one column, and SUMX exposed the truth.
✓ A good measure is also an audit tool for the data. When a number doesn't add up — it's not always an error in the formula. Sometimes it's an error in the source.
Chapter VII · Filter Context and CALCULATE
Filter Context — one measure, a different value in every cell
we wrote the [Total Sales] once. How does it show four different numbers in one table?
category
[Total Sales]
Bikes
$94.6M
Components
$11.8M
Clothing
$2.1M
Accessories
$1.3M
Power BI looks at where the cell sitsThe Bikes row? The FY2020 column?
Builds a "filter bubble" for the cellOnly the rows relevant to it — that's the Filter Context.
Runs the measure inside the bubbleIn the Bikes row, the measure "sees" only bikes.
So a measure doesn't "break" from filters — it lives on them. Every cell gets its own bubble, and the measure runs inside it. CALCULATE is the way to intentionally change the bubble.
Chapter VII · Filter Context and CALCULATE
CALCULATE — the king of DAX
Runs an existing measure — but first changes the filter context according to your conditions. The most important function in DAX.
has-ALL no condition and nowhere to write one. It only sets the starting point: the original table, clean of slicers.
The page has an active Color = "Red" slicer, and it shows $21,597,891
DAX
All Products Sales = CALCULATE ( [Total Sales], ALL ( Product ) )
$21,597,891What the page shows (Red only)
$109,809,274What ALL returns — everything
variation
What it deletes
ALL( Product )
A whole table — all the filters from it. A slicer on the Date table doesn't touch it
ALL( Product[Color] )
A single column — releases only the color and leaves the rest
ALL( 'Date' )
For all periods
REMOVEFILTERS()
Everything from everything
✓When is ALL alone enough? When the question is "and how much is that in total?" - as a basis for comparison against the filtered value. For any question with a condition ("only expensive ones", "only red ones") - ALL alone won't help: it opens everything and selects nothing.
ALL alone = half the job: Delete, not select. You want to delete and also choose again? Put it inside FILTER — in the next topic.
Chapter VII · Filter Context and CALCULATE
FILTER — the guard that goes row by row
FILTER runs row by row over the table you gave it, and returns a new table — only the rows that evaluated TRUE.
ALL opened up all 397 products, and FILTER picked 13 of them — regardless of the slicer.
The trap from the data: Slicer on Product[Category] = "Clothing", and asking for red. With FILTER ( Product, … ) you'll receive $0 — because no red clothing was sold at all. With FILTER ( ALL ( Product ), … ) You'll get $21,597,891. When the measure is "broken" — it's almost always missing ALL.
✓The two questions before every measure: (a) Do I have a condition to check? ← FILTER, or a boolean if the condition is simple. (b) Ignore what's filtered on the page? ← ALL inside. Two "yes" answers = FILTER ( ALL ( … ), … ).
Chapter VII · Filter Context and CALCULATE
Negation, release and percent of total
Three moves that close out the chapter: negation, release, and percent of the total.
$15,188,748Everything that isn't bicyclesNon Bikes
Releasing filters — and what percent of the grand total
DAX
All Sales = CALCULATE ( [Total Sales], ALL ( Sales ) )
% of Total = DIVIDE ( [Total Sales], [All Sales] )
86.2%The share of bikes out of total sales
Layers again:[Total Sales] ← [All Sales] ← [% of Total]. Each measure stands on the shoulders of the previous one.
Chapter VIII · Window Functions
The idea — one list, seven questions
Every cell in the table lives alone in a bubble. In the Canada row, the measure only sees Canada — it doesn't know that the US is before it, that Australia is after it, or that it's second out of six. A window function is the tool that lets a cell look at its neighbors in the list.
The ordering row — the list the whole chapter is built on
The six countries in the model, sorted by sales. Each function in the chapter asks a different question about the exact same list.
place
Country
[Total Sales]
1
United States
$62,997,591
2
Canada
$16,355,770
3
Australia
$10,655,336
4
United Kingdom
$7,670,721
5
France
$7,251,556
6
Germany
$4,878,300
Seven functions, seven questions
function
The question it asks
ORDERBY
By what they line up in order
PARTITIONBY
One long line, or several separate lines
ROWNUMBER
"What is my serial number?"
RANK
"What place did I get?"
INDEX
"Who is in position N?"
OFFSET
"Who is my neighbor?"
WINDOW
"Give me a slice from the sorted list"
Three rules that prevent 95% of the issues
01
The table name — from autocomplete
The model has duplicate tables (SalesTerritory and also Sales data_Territory). Start typing and choose from the list. A measure pointing to a table the visual doesn't use = blank.
02
The measure is always in square brackets
[Total Sales] and not SUM ( … ) raw. SUM inside a window function ignores context rows — and all countries come out the same.
03
The blank rule — the level of granularity
The functions only respond when the visual is at the row's level of detail. The lists here are at the Country level ← the measures live in the table whose rows are Country. In a card or a Total row — different behavior.
Note on names: In the original guide the measure is called [total salse] and the table SalesTerritory. Here we'll use[Total Sales] — the measure we built in Chapter IV — and in the same SalesTerritoryIf your names are different, just swap them and nothing else.
Chapter VIII · Window Functions
1 · ORDERBY — what determines the order
ORDERBY is the first decision of every window function: what to order the queue by. It doesn't stand on its own — it's always an argument inside a hosting function.
The same money measure, two reversed words — and two orders
Without writing DESC you will get the smallest country first — the opposite of what you would expect from a sales ranking, and the opposite of the old RANKX. Always write the direction explicitly.
02
Sorting by money requires a measure
SUM ( … ) Raw inside ORDERBY doesn't carry context, and all rows would come out identical. You must [Total Sales].
03
ORDERBY doesn't filter
No country disappears. It only determines the order of standing in line before the numbers are handed out.
and what happens when you don't write a direction at all
✓ The ASC default kicks in: Germany = 1 · France = 2 · United Kingdom = 3 · Australia = 4 · Canada = 5 · United States = 6. The weakest country got rank 1. This is the most common issue in this section.
Sorting and ties: ROWNUMBER is committed to a unique number. In our data there are no two countries with the same total, so one sort key is enough. If an exact tie shows up — the engine won't be able to decide who's 3 and who's 4. RANK, on the other hand, knows how to share a rank on a tie.
Chapter VIII · Window Functions
2 · PARTITIONBY — when the count resets
PARTITIONBY takes a single ordering row and breaks it into separate leagues — a competition for each group, with its own place 1. It doesn't sort and doesn't filter; it just determines Where they reset.
From one order row to three leagues
DAX
Row In Group = ROWNUMBER ( ALL ( SalesTerritory[Country], SalesTerritory[Group] ), ORDERBY ( [Total Sales], DESC ), PARTITIONBY ( SalesTerritory[Group] ) )
Group
Country
[Total Sales]
Row In Group
Europe
United Kingdom
$7,670,721
1
Europe
France
$7,251,556
2
Europe
Germany
$4,878,300
3
North America
United States
$62,997,591
1
North America
Canada
$16,355,770
2
Pacific
Australia
$10,655,336
1
01
The dividing column must be in ALL
SalesTerritory[Group] must also appear inside the ALL — it's part of the list of contenders.
02
The visual must show both levels
Both Group and Country in the rows. Otherwise the engine cannot identify a single "me" — and returns blank.
The same helper works across the whole family — here on WINDOW: a running total that resets on every continent
DAX
Running Total In Group = CALCULATE ( [Total Sales], WINDOW ( 1, ABS, 0, REL, ALL ( SalesTerritory[Country], SalesTerritory[Group] ), ORDERBY ( [Total Sales], DESC ), PARTITIONBY ( SalesTerritory[Group] ) ) )
Group
Country
[Total Sales]
Running Total In Group
Europe
United Kingdom
$7,670,721
$7,670,721
Europe
France
$7,251,556
$14,922,277
Europe
Germany
$4,878,300
$19,800,577
North America
United States
$62,997,591
$62,997,591
North America
Canada
$16,355,770
$79,353,361
Pacific
Australia
$10,655,336
$10,655,336
✓Sanity check: The last row of each league equals the total of the entire league — Europe: $19,800,577.
Chapter VIII · Window Functions
3 · ROWNUMBER — everyone gets their own number
ROWNUMBER goes through the order-by row and sticks a numbered label on each contestant — 1, 2, 3… necessarily unique: even if two contestants are completely tied, each one gets their own number.
Numbering by sales — sorted from the opening
DAX
Row By Sales = ROWNUMBER ( ALL ( SalesTerritory[Country] ), ORDERBY ( [Total Sales], DESC ) )
Country
[Total Sales]
Row By Sales
United States
$62,997,591
1
Canada
$16,355,770
2
Australia
$10,655,336
3
United Kingdom
$7,670,721
4
France
$7,251,556
5
Germany
$4,878,300
6
#
what's written
What does this mean
1
ALL ( SalesTerritory[Country] )
The contenders: all the countries. The ALL clears the row filter — without it, in the Canada row there's one contender and no one to rank against
2
ORDERBY ( [Total Sales], DESC )
The order: money, largest to smallest. One sort column, direction written explicitly
3
ROWNUMBER ( … )
The answer: the serial number of the country in the current row
map: Rows ← 1–6 · Total row ← empty (there's no "current country" — recommended to turn off Totals). If a Corporate HQ row shows up — a dimension value with no sales — add a visual filter Total Sales is not blank.
The same function, a different sort key: alphabetical numbering
✓ Australia = 1 · Canada = 2 · France = 3 · Germany = 4 · United Kingdom = 5 · United States = 6. The exact same function, just a different sort column — and all the numbers change. The numbers aren't determined by the function, but by the list it receives.
Chapter VIII · Window Functions
4 · RANK — rank, not a sequential number
Sounds like ROWNUMBER, but the question is different: not "what's my number in line" but "What place did I get in the competition". The difference shows up in a tie.
The basic sales ranking
DAX
Country Rank = RANK ( ALL ( SalesTerritory[Country] ), ORDERBY ( [Total Sales], DESC ) )
Country
[Total Sales]
Country Rank
United States
$62,997,591
1
Canada
$16,355,770
2
Australia
$10,655,336
3
United Kingdom
$7,670,721
4
France
$7,251,556
5
Germany
$4,878,300
6
Here's how it's taught: Put the money column next to the ranking column — you can see with your own eyes that the largest number gets 1. Filter with a slicer to a specific year: the money column changes, and the ranking rearranges itself automatically — because ORDERBY sorts by the measure, and the measure lives in a bubble.
and what happens in a tie
First parameter (optional)
behavior
SKIP — the default
Two countries tied for place 2 ← the one after them gets place 4. We skip place
DENSE
Two countries tied for place 2 ← the next one gets 3. No gaps
In our data there's no tie, so both versions are identical — but when ranking products by quantities this happens all the time.
Ranking within a group — the five US regions
DAX
Region Rank in Country = RANK ( ALL ( SalesTerritory[Region], SalesTerritory[Country] ), ORDERBY ( [Total Sales], DESC ), PARTITIONBY ( SalesTerritory[Country] ) )
Country
Region
[Total Sales]
Rank
United States
Southwest
$24,184,610
1
United States
Northwest
$16,084,943
2
United States
Central
$7,909,009
3
United States
Southeast
$7,879,655
4
United States
Northeast
$6,939,374
5
✓ The rest of the countries — a single region, always 1. Notice how close Central and Southeast are — only a $29K difference. Natural candidates for a tie in a single year.
Chapter VIII · Window Functions
RANK vs. RANKX — two worlds
The veteran RANKX (2015) and the new RANK (2022) answer the same question — "what place did I get" — with opposite mechanisms. On our model both return an identical ranking, but the edge-case behavior is completely different.
aspect
RANKX — the veteran iterator
RANK — the window function
the mechanism
Compares values: calculates my measure, calculates for each competitor, counts how many are greater than mine
Identifies a row: builds a sorted list and locates "me" in it
Default direction
DESC — largest first, as you'd expect from a ranking
ORDERBY defaults to ASC — you must write DESC yourself
tie
Fifth parameter: SKIP / DENSE
First parameter: SKIP / DENSE
leagues
No — you work around it with cumbersome filtered tables
built-in: PARTITIONBY
Level of detail
Works at every level — card, Total, hierarchy
The blank rule: applies only at the list level. Card ← blank
In the Total row
Shows 1 — the grand total beats everyone
empty
The classic pitfall
Raw SUM instead of [measure] ← no context transition ← everyone in place 2
Wrong or duplicate table name ← empty column
When to choose
Fast, fault-tolerant ranking in every visual
leagues, full control over order and tiebreakers, working alongside the rest of the family
The two minimal syntaxes, side by side
DAX
Rank Old = RANKX ( ALL ( SalesTerritory[Country] ), [Total Sales] )
Rank New = RANK ( ALL ( SalesTerritory[Country] ), ORDERBY ( [Total Sales], DESC ) )
The sentence for the class: RANKX asks "how many sell more than me?" — arithmetic on values, works everywhere. RANK asks "where do I stand in the row?" — position identification, which requires an identified row. Same ranking, opposite philosophy — and so the failure modes are opposite too.
Chapter VIII · Window Functions
5 · INDEX — who's in position N
Until now, every row asked about itself. INDEX reverses the direction: you set a position — 1 for the first, -1 for the last — and it retrieves the row sitting there.
How much does the champion sell — and how far am I from it
✓Canada is "second" — but a quarter of the US. Rank tells you position; INDEX gives you proportion.
#
what's written
What does this mean
1
INDEX ( 1, … )
The requested rank: first in the ordering row — the US
2
CALCULATE ( [Total Sales], … )
The shift: calculate the measure within the row we fetched — not the row I'm currently on
INDEX + PARTITIONBY — the champion of my league
DAX
Group Leader Sales = CALCULATE ( [Total Sales], INDEX ( 1, ALL ( SalesTerritory[Country], SalesTerritory[Group] ), ORDERBY ( [Total Sales], DESC ), PARTITIONBY ( SalesTerritory[Group] ) ) )
✓ Each row shows the sales of its continent's champion — Europe: $7,670,721 (UK) · North America: $62,997,591 (USA) · Pacific: $10,655,336 (Australia). Swap the 1 for -1 and get the weakest: in Europe, Germany ($4,878,300).
How to explain: RANK answers "where am I"; INDEX answers "show me who's in position 1". Same sort order, the direction of the question flips. And once every row has its leader's value — any "distance from the champion" comparison is a single DIVIDE.
Chapter VIII · Window Functions
6 · OFFSET — the neighbor, a step up or down
OFFSET moves by a relative distance from me in the sort order: -1 = the row above me, +1 = the row below me. This is the function of "How much do I need to overtake the next in line" — compared to the neighbor, not the champion.
map: The blank in the US row is intentional and documented — the first has no "above me". That's the edge of the window, not a bug. Say this to the class before someone asks.
The Lego principle — measure on measure: the gap from the country above me
DAX
Gap To Above = [Total Sales] - [One Above Sales]
Country
Gap To Above
Canada
−$46,641,820 — a chasm from the US
Australia
−$5,700,434
United Kingdom
−$2,984,615
France
−$419,165 — breathing down Britain's neck
Germany
−$2,373,255
✓The business insight: The tight chase in the league is happening at places 4-5, not at the top. OFFSET compares me to my neighbor - a different target for each row, one that moves along with me. It's a completely different business question from "how far am I from the leader".
Chapter VIII · Window Functions
7 · WINDOW — a segment of the ordered set
Everything we've seen so far returned a single row. WINDOW returns strip: from a starting point to an end point. Every point is either absolute or relative to me (REL) or absolute (ABS).
Running total — from the start of the list up to me
An absolute start: place 1 — USA. Not dependent on the row I'm standing on
2
3, ABS
Absolute closure: place 3 — Australia. Two fixed points ← identical window for all rows ← a constant column
3
The comparison to cumulative
Cumulatively the ending was 0, REL — moves with me. One word (REL↔ABS) turns a moving window into a fixed window
✓ $90,008,697 is exactly the Running Total for Australia (rank 3) — two different windows meeting at the same cell. A beautiful mutual validation.
The logic of the four numbers
the join
What we get
1, ABS → 0, REL
Running total — a window that moves with me and grows
1, ABS → 3, ABS
Fixed top 3 — a window that stays in place
-1, REL → 0, REL
Me and the neighbor above me
REL = "from me onward", a window that moves with me · ABS = "from the fixed spot," a window that stays in place. Play with the two pairs of numbers — and get a whole family of measures from the same order row.
Chapter VIII · Window Functions
Family summary — seven functions on one line
Seven functions, fourteen examples, and one list. Here's the whole family on the Canada row — so you can see the differences in a single cell.
function
the question
What will appear in the Canada row
ORDERBY
What determines the order (ASC by default!)
Rank Low→High = 5
PARTITIONBY
When the count resets
Running Total In Group = $79,353,361
ROWNUMBER
What is my serial number
Row By Sales = 2
RANK
What place did I take (ties split)
Country Rank = 2
INDEX
Who is in position N
Leader Sales = $62,997,591
OFFSET
Who is my neighbor
One Above = $62,997,591 · Gap = −$46.6M
WINDOW
Give me a segment from the sorted array
Running Total = $79,353,361
The two helpers in the sentence: ORDERBY — by what it's sorted · PARTITIONBY — when it resets. Two answers to two different questions, and both live within the five main functions: ROWNUMBER, RANK, INDEX, OFFSET, WINDOW.
✓ Every cell in the chapter was verified in pandas against AdventureWorks_Sales.xlsx — 121,253 rows, $109,809,274.
Chapter IX · Advanced DAX — 20 functions
Chapter map — 20 advanced functions
Twenty functions that pick up where CALCULATE and the window family left off. There are no basic functions here — each one solves a problem that can’t be solved without it.
family
functions
The problem they solve
Fine control over context
KEEPFILTERS · ALLSELECTED · ALLEXCEPT
not to override what the user selected, and to calculate a share within a group
Relationships at runtime
USERELATIONSHIP · CROSSFILTER · TREATAS
To activate an inactive relationship, reverse the filter direction, or filter without a physical relationship at all
To build virtual tables, summarize, and calculate only on the leaders
Context awareness
ISINSCOPE · SELECTEDVALUE · USERPRINCIPALNAME
To know which level I'm at, what the user chose, and who they even are
Values and keys
COALESCE · COMBINEVALUES · CONTAINSROW · EARLIER
Handling blanks, building a composite key, and checking membership
Structure and helper data
PATH · PATHITEM · DATATABLE
Parent-child hierarchies and tables written directly in code
Transparency about the numbers: Not every function here can be demonstrated on AdventureWorks — some require a passive relationship in the model, a parent-child hierarchy, or a Power BI Service environment. Where there is a verified number from the file — it appears. Where there isn't — only the syntax and behavior appear, without inventing a result.
✓The rule that ties the whole chapter together: Each of these functions ultimately comes down to a change of context — or of the list of contenders. If you understood Filter Context from Chapter VII, you have all the foundation you need.
Chapter IX · Advanced DAX — 20 functions
Fine control over context — KEEPFILTERS · ALLSELECTED · ALLEXCEPT
Three functions that replace ALL's "all or nothing" with precise control: trim instead of overwrite, respect the user's selection, and clear everything except one column.
KEEPFILTERS — to intersect, not override
CALCULATE's default behavior is to override: a condition on a column drops any existing filter on that same column. KEEPFILTERS turns this into an intersection.
On the page there's an active slicer Product[Color] = "Red" showing $21,597,891
When each one: "What percent of the whole business?" ← ALL. "What percent of what I’m looking at right now?" ← ALLSELECTED. This is the function behind every "share of selection" measure.
ALLEXCEPT — clears everything except…
Removes all filters from a table, except the columns you specified. In practice: "give me the total of the group I belong to".
In the table whose rows have both Group and Country
DAX
Group Total = CALCULATE ( [Total Sales], ALLEXCEPT ( SalesTerritory, SalesTerritory[Group] ) )
% of Group = DIVIDE ( [Total Sales], [Group Total] )
Group
Country
[Total Sales]
Group Total
Europe
United Kingdom
$7,670,721
$19,800,577
Europe
France
$7,251,556
$19,800,577
Europe
Germany
$4,878,300
$19,800,577
North America
United States
$62,997,591
$79,353,361
North America
Canada
$16,355,770
$79,353,361
Pacific
Australia
$10,655,336
$10,655,336
✓ The exact same numbers we saw in the last row of each league in Running Total In Group. Two completely different approaches, the same result — mutual verification.
Chapter IX · Advanced DAX — 20 functions
Relationships at runtime — USERELATIONSHIP · CROSSFILTER · TREATAS
Three functions that touch the relationships themselves — for calculation time only, without changing the model.
USERELATIONSHIP — activates a passive relationship
There can be more than one relationship between two tables, but only one is active. USERELATIONSHIP activates the passive one — within the measure only.
The classic pattern: the same date table, once by order date and once by shipping date
DAX
Sales By Ship Date = CALCULATE ( [Total Sales], USERELATIONSHIP ( Sales[ShipDateKey], 'Date'[DateKey] ) )
Can't be demonstrated on our model: In AdventureWorks as we built it, there's a single relationship Sales[OrderDateKey] → Date[DateKey], and there's no second passive relationship. To try it — add another relationship in the model fromShipDateKey FDon't mark it as active. Two rules: the relationship must exist in the model, and USERELATIONSHIP must sit inside CALCULATE.
CROSSFILTER — changes filter direction
temporarily changes the filter direction of an existing relationship — Single, Both, or None — only within the calculation.
DAX
Products Sold = CALCULATE ( DISTINCTCOUNT ( Product[ProductKey] ), CROSSFILTER ( Sales[ProductKey], Product[ProductKey], BOTH ) )
The normal direction in the model is from the dimension to the fact. With BOTH The filter also flows back — for example "how many products were sold at all in the selected year". With NONE disconnect the relationship for a moment. This is the measured alternative to global bidirectional filtering, which we learned in lesson 2.
TREATAS — a virtual relationship
applies values from one table as a filter on a column in another table — even when there is no physical relationship between them.
✓ The exact same result as Product[Category] IN { "Clothing", "Accessories" } From Chapter VII. The difference starts when the list comes from a completely different table — a targets table, a parameters table, or the result of a calculation. Then TREATAS is the only way.
Two functions that live in the query world — DAX Studio, Performance Analyzer, and calculated tables — not inside a regular measure.
SUMMARIZECOLUMNS — the modern aggregation
It groups by columns, adds calculated columns, and accepts filters — all in a single call optimized for VertiPaq. It's the one that actually produces almost every visual in Power BI.
Exactly the table we saw in the window functions chapter
Why not the old SUMMARIZE: SUMMARIZECOLUMNS handles Context Transition on its own, accepts filters as an argument, and skips empty combinations. SUMMARIZE with calculated columns inside it is now considered an anti-pattern.
ROLLUPADDISSUBTOTAL — automatic summary rows
Written inside SUMMARIZECOLUMNS and adds Subtotal and Total rows, with a flag column that tells each row whether it's a summary or a detail.
DAX
SUMMARIZECOLUMNS ( ROLLUPADDISSUBTOTAL ( SalesTerritory[Group], "Is Group Total", SalesTerritory[Country], "Is Country Total" ), "Sales", [Total Sales] )
✓ The result will add rows with Is Group Total = TRUE — there you'll see $19,800,577 for Europe, $79,353,361 for North America, and $10,655,336 for Pacific. These are exactly the numbers from ALLEXCEPT.
Chapter IX · Advanced DAX — 20 functions
Table functions — TOPN · EXCEPT · GENERATE
Three functions that return a virtual table — which is then fed into CALCULATE to calculate over just that table.
TOPN — only the top ones
Sum of the three largest countries
DAX
Top 3 Countries = CALCULATE ( [Total Sales], TOPN ( 3, ALL ( SalesTerritory[Country] ), [Total Sales], DESC ) )
$90,008,697The three largest countriesTOPN
82.0%Their share of the whole business
✓ exactly the same number we got from WINDOW ( 1, ABS, 3, ABS, … ) in the previous chapter. Two completely different functions, the same answer — that's the best sanity check there is.
Note the tie: TOPN doesn't break ties. If places 3 and 4 are exactly equal — it will return four rows, not three. This happens all the time when ranking products by quantities.
EXCEPT — what's here and not there
All countries except the US
DAX
Countries Without US = EXCEPT ( VALUES ( SalesTerritory[Country] ), { "United States" } )
✓ Returns a table of five rows: Canada, Australia, United Kingdom, France, Germany. A set operation — also removes duplicates.
The real usage: "Which customers bought last year and didn't buy this year?" — EXCEPT Between two lists VALUES ( Sales[CustomerKey] ) in different time contexts. This is the classic churn pattern.
GENERATE — a controlled Cross Join
For each row in the first table, the engine re-evaluates the second table — which can use the values of the current row — and returns the combination.
DAX
Category By Year = GENERATE ( VALUES ( Product[Category] ), VALUES ( 'Date'[Fiscal Year] ) )
Here it's a simple Cross Join — 4 categories × 4 fiscal years. The real power shows up when the second table depends on the first, for example "the three biggest products In every category" — GENERATE with TOPN inside.
Chapter IX · Advanced DAX — 20 functions
Context and user awareness — ISINSCOPE · SELECTEDVALUE · USERPRINCIPALNAME
Three functions that let a measure know where it is, what's selected, and who's looking at it.
ISINSCOPE — What level am I at?
returns TRUE if the column is at the current level of calculation. This is the tool for writing a measure that behaves differently at the detail row, the subtotal row, and the Total row.
If the context is reduced to a single value — returns it. If not — returns the default. An end to the clunky pattern of IF ( HASONEVALUE ( … ), VALUES ( … ) ).
DAX
Report Title = "Sales · " & SELECTEDVALUE ( SalesTerritory[Country], "All Countries" )
✓ One country selected in the slicer ← "Sales · Canada". Two selected, or none ← "Sales · All Countries". Put the measure in a card title — and the title updates on its own.
USERPRINCIPALNAME — who's logged in
Returns the email address of the logged-in user. This is the basis for Dynamic Row-Level Security (Dynamic RLS).
DAX
// Row-Level Security rule on a security table UserSecurity[Email] = USERPRINCIPALNAME ()
Cannot be demonstrated in Desktop: In Power BI Desktop the function returns the local username, and the real value is obtained only in Power BI Service. You test it with View as roles.
Four small functions that solve everyday problems: blanks, combined keys, membership checks, and nested row context.
COALESCE — the first value that isn't blank
DAX
Sales Or Zero = COALESCE ( [Total Sales], 0 )
In a table whose rows are Country, a country with no sales will show blank. With COALESCE it will show 0 — and more importantly, the row will stop disappearing from the visual. Much more readable thanIF ( ISBLANK ( … ), 0, … ) because the expression is calculated only once.
✓ the result: United States|Southwest. The separator is guaranteed not to accidentally appear inside the values, so this is safer than concatenating with &. The common use: a composite key for row-level security, or a virtual relationship on two columns with TREATAS.
Context for IN: the operator IN that we used in Chapter VII is exactly syntactic sugar for CONTAINSROW. The real power begins when you check a combination of several columns together — CONTAINSROW ( Table, val1, val2 ) — something IN doesn't know how to do on a virtual table.
EARLIER — Outer Row Context
In a calculated column you can open a second row context (for example inside FILTER). EARLIER lets you refer to the row's value from the outer context.
A calculated column in the Product table: the product's ranking within its category
How to read this: The FILTER opens a new row context that runs over all 397 products; EARLIER returns the values of the product thatthat we're standing on in the column. Count how many products in the same category are more expensive than mine, and add 1.
✓Professional note: Since variables (VAR) were introduced, EARLIER is almost never needed — you write VAR CurrentCat = Product[Category] and use the variable. It is still important to know it, because it appears in every DAX code written before 2015.
Chapter IX · Advanced DAX — 20 functions
Structure and helper data — PATH · PATHITEM · DATATABLE
The last two functions deal with structure: a hierarchy hidden in a flat table, and a table written directly in code.
PATH and PATHITEM — parent-child hierarchy
A flat employee table with EmployeeKey andManagerKey hides an organizational tree inside it. PATH unfolds it into a string, and PATHITEM extracts a level from it.
Can't be demonstrated on our model: AdventureWorks_Sales.xlsx doesn't have an employee table with a manager column. The pattern itself is identical in every parent-child model — an org chart, a chart of accounts, or categories within categories. The columns that get created are placed in a hierarchy in the visual.
DATATABLE — a table written in code
Defines a static table directly in DAX, with no external source at all. Perfect for mapping scales, parameter tables, and helper data.
A targets table by category — written once and connected via TREATAS
This table doesn't come from Power Query and doesn't refresh — it's part of the code. Notice this is exactly the alternative toEnter Data that we used for the table Measures: there via the interface, here via code. The advantage: the table is documented in code and can be copied between files.
✓The winning combination: DATATABLE creates the targets table, TREATAS connects it to categories without a physical relationship, andDIVIDE Calculates the percent that met the target. Three functions from this chapter — one complete business measure.
Chapter IX · Advanced DAX — 20 functions
Chapter summary — 20 functions in one table
Twenty functions, one line each. This is the chapter's orientation table.
#
function
What it does
Verified on the file
1
KEEPFILTERS
Intersecting instead of overriding an existing filter
✓ $21,597,891
2
ALLSELECTED
Removes row filtering and respects the user's selection
✓ 86.2%
3
ALLEXCEPT
Clears everything except the specified columns
✓ $19,800,577
4
USERELATIONSHIP
Activates an inactive relationship within the calculation
— requires an inactive relationship
5
CROSSFILTER
Temporarily changes the filter direction
— requires checking in the model
6
TREATAS
A virtual relationship between tables without a physical relationship
✓ $3,389,671
7
EARLIER
A value from an outer row context
— pattern, replaced with VAR
8
SUMMARIZECOLUMNS
Modern, query-optimized aggregation
✓ Countries table
9
ISINSCOPE
What hierarchy level am I at
✓ $19,800,577
10
SELECTEDVALUE
The single value selected, or a default
— behavior
11
PATH · PATHITEM
Unfolds a parent-child hierarchy
— there's no employee table
12
COALESCE
The first value that is not empty
— behavior
13
COMBINEVALUES
A composite key with a safe separator
— behavior
14
CONTAINSROW
Whether the combination exists in the table
✓ Same as IN
15
TOPN
Only the top N rows
✓ $90,008,697
16
GENERATE
A controlled Cross Join between two tables
— pattern
17
ROLLUPADDISSUBTOTAL
Automatic summary rows in the query
✓ $79,353,361
18
USERPRINCIPALNAME
The logged-in user — the basis for dynamic RLS
— only in Service
19
EXCEPT
What's in one table and not in the other
✓ Five countries
20
DATATABLE
A static table written in code
— behavior
The three joins worth remembering: TOPN inside CALCULATE = "top performers only" · DATATABLE + TREATAS = a target table with no physical relationship · SUMMARIZECOLUMNS + ROLLUPADDISSUBTOTAL = a complete summary report in a single query.
✓ Every function in this chapter ultimately comes down to changing the context or changing the list of contenders. If Filter Context is clear - there's nothing conceptually new here, just sharper tools.
Chapter X · Summary and Self-Check
Golden rules for writing DAX
Six habits that separate code that works from code that's maintainable.
01
Measure by default
A calculated column only when you need to filter, group, or put it on an axis. Otherwise — a measure.
02
DIVIDE, not a slash
Prevents the visual from crashing on division by zero.
03
Layered measures
A measure calling a measure. One place to fix, clean code.
04
Clear names
Total Sales, not Measure 1. The name appears in the visual.
05
CALCULATE for filtering
Not to duplicate manual logic in every measure again.
06
Measures table
Consolidating all measures in one place — they don't belong to any data table.
and in addition: The golden rule from Chapter I — push all processing back. What can be done at the source or in M, don't do in DAX.
Chapter X · Summary and Self-Check
What we learned — ten points
01
Two languages, two engines
M prepares the data once per refresh. DAX calculates on them with every interaction.
02
the golden rule
Push every bit of processing back: the database ← Power Query ← DAX.
18,485 and not 18,484 — always ask who's hiding in the keys.
06
An iterator for row-wise multiplication
SUM takes a single column. Quantity × price needs SUMX.
07
Filter Context
Every cell is a filter bubble, and the measure runs inside it. That's why the same measure shows different values.
08
CALCULATE changes the filter bubble
FILTER checks who passes · ALL determines where you start from · CALCULATE combines and finishes the job.
09
Window functions
let a cell look at its neighbors in the list: ROWNUMBER, RANK, INDEX, OFFSET, WINDOW.
10
The two helpers
ORDERBY — what to sort by (default ASC!) · PARTITIONBY — when to reset.
and a good measure is also a critique tool: Ask about the $527,508.
205 questions across five banks — and 12 practice assignments
Chapter X · Summary and Self-Check
Quiz — 39 questions on M and DAX
One question at a time, four options, immediate feedback with an explanation. All the questions deal only with M and basic DAX.
What correctly describes the division of roles between M and DAX?
explanation: M is Power Query's ETL language, and DAX is the data model's calculation language.
When does M code run?
explanation: After the refresh the work is done and the table is "baked in" — it's fixed until the next refresh.
When does DAX run?
explanation: This is what makes a measure "live": every slicer and every click triggers recalculation.
Which language is case-sensitive?
explanation: In M, Table.AddColumn works and table.addcolumn will throw an error. DAX is not case-sensitive.
What the structure contains let … in in M?
explanation: Between let and in, it's defined step by step, and each step relies on the previous one.
According to the golden rule (Roche's Maxim), where is it best to perform a transformation?
explanation: Priority order: the database ← Power Query ← DAX. The further back, the lighter and faster the model.
You want a new table that continues from an existing table's steps and updates with it. What do you choose?
explanation: Duplicate creates a disconnected copy; Reference continues from the original query, and any change to it flows onward.
You derived DimCustomer from Sales and got 18,485 rows instead of 18,484. What happened?
explanation: The key -1 represents "Not applicable". Always ask: who's hiding in my keys?
What is the first condition for a valid date table?
explanation: Without continuity, Time Intelligence functions will return incorrect results. In our case: 1,461 consecutive days.
Why do you need to check Mark as date table?
explanation: The marking tells the engine that this is the model's date table and what its unique date key is.
Which three objects does DAX know how to create?
explanation: Most of the day-to-day work is measures; calculated tables are relatively rare.
Need to sum numbers for a visual. What do you write?
explanation: The golden rule: summing numbers ← Measure. Classifying rows ← Column.
What's true about a calculated column?
explanation: The column is computed once on refresh and then sits in the model — exactly like a column that came from the source.
What's true about a measure?
explanation: A measure doesn't "break" from filters — it lives off them. That's the whole idea of Filter Context.
You saw in the formula [Total Sales] — square brackets without a table name. What is that?
explanation: A column is always written with the table name before it: Sales[Sales Amount]. Brackets alone = a measure.
What does SUM take as an argument?
explanation: SUM does not accept an expression. To multiply two columns within a row you need an iterator like SUMX.
How does AVERAGE treat empty cells?
explanation: This is a point that surprises many: Blank simply doesn't participate in the calculation.
Does MAX know how to work on a date column?
explanation: MIN is the reverse twin, with the exact same syntax.
What does COUNTROWS accept?
explanation: and so it's also the basis for conditional counts: put a FILTER inside it and get a count by condition.
One customer bought 25 times. What will COUNT and DISTINCTCOUNT return on CustomerKey?
explanation: and so for the question "how many customers?" the answer is always DISTINCTCOUNT.
Why use DIVIDE instead of a slash?
explanation: one cell with a zero denominator is enough to bring down an entire visual. Professional measures don't use the slash operator.
What's the third parameter of DIVIDE?
explanation: It's optional. Omitted it? A silent Blank will be returned.
What's the advantage of writing measures in layers (a measure that calls a measure)?
explanation: This also makes the code readable: Margin % looks like its own business definition.
A calculated column with Sales[Sales Amount] - Sales[Total Product Cost] — in what context did it run?
explanation: The engine goes over each row in turn and computes its value, then stores it physically.
In IF you omitted the third argument. What will happen to rows that didn't meet the condition?
explanation: The else argument is optional — the square brackets in the documentation mark exactly that.
What makes the trick work SWITCH ( TRUE (), … )?
explanation: Without this trick, SWITCH only knows how to compare exact values.
In SWITCH you wrote earlier >= 1000 and after it > 5000. What will happen?
explanation: The waterfall rule: always from strictest to loosest, from highest to lowest.
In which direction does RELATED work?
explanation: Every sales row has exactly one product, so this direction is unambiguous.
What does RELATED require in order to work?
explanation: Without an active context it won't even be suggested in autocomplete.
What does RELATED take as an argument?
explanation: For example RELATED( Product[Category] ) — not RELATED( Product ).
Which operator concatenates text in DAX?
explanation: For example Product[Model] & " · " & Product[Color] — perfect for labels in slicers and tooltips.
You want to classify products into price tiers. Where's best to write the column?
explanation: A column on 397 rows serves all 121,253 sales through the relationship. Less memory, same result.
Why do you need SUMX to calculate quantity × price?
explanation: The iterator evaluates the expression within every row and only then combines the results.
The difference between SUMX(Qty × Unit Price) and SUM(Sales Amount) came out to $527,508. What is that?
explanation: The discount percentage column showed 0 in every row, but 3,282 rows received a discount — all of them in the Reseller channel.
The same measure shows a different value in every row of the table. Why?
explanation: This is the Filter Context: the cell's position determines which rows the measure "sees".
What does CALCULATE do, in order?
explanation: In a conflict — CALCULATE's filter overrides the report's filter on the same column.
You wrote two conditions inside CALCULATE, separated by a comma. What does that mean?
explanation: The comma between conditions is an automatic AND; there's no need to write it.
what does ALL ( Product ) Inside CALCULATE?
explanation: A slicer on the Date table won't be affected. For everything from everything there's REMOVEFILTERS().
When is FILTER mandatory, when a boolean condition alone won't do?
explanation: The engine recalculates the measure for every row (Context Transition). For a simple condition on a column — it is boolean, and that is faster.
You haven't answered questions yet — 0 out of 39
Concepts and syntax — no data file needed
Chapter X · Summary and Self-Check
DAX Question Bank — 25 Concept Questions
Questions about DAX concepts and syntax — what each function takes, how it behaves, and when to choose it. There are no questions here that require the data file: you can answer all of them without opening Power BI.
The table and column names that appear in the questions are taken from the model we worked with in the lesson, but no question asks for a numeric result from the file.
Which formula returns the total sales in the model?
explanation: SUM takes a single numeric column in the format table[column]. The result: $109,809,274.
what returns COUNTROWS ( Product )?
explanation: The products table is a small dimension — only 397 products.
what returns COUNTROWS ( 'Sales Territory' )?
explanation: The zones table is the smallest dimension in the model — 11 rows.
How do you correctly define the profit measure?
explanation: A measure calling a measure — that's the layering principle. The third option is the definition of a calculated column, not a measure.
Why not write [Total Profit] / [Total Sales]?
explanation: DIVIDE returns Blank instead of an error — and in a visual that's the difference between an empty cell and a broken report.
What the difference between SUMX ( Sales, Qty * Unit Price ) and SUM ( Sales[Sales Amount] )?
explanation: Theoretical revenue minus what was actually paid — and that's exactly the value of the discounts.
Why SUM on the column Unit Price Discount Pct won't reveal the discounts?
explanation: The data "lied" in one column — and only SUMX revealed the truth.
In which sales channel were all the discounts given?
explanation: the same channel that carries the technical key CustomerKey = -1.
Where is the column written Profit = Sales[Sales Amount] - Sales[Total Product Cost]?
explanation: A calculated column must sit in the table it reads its values from.
In SWITCH you wrote earlier >= 1000 and only after it > 5000. What will happen to a $20,000 transaction?
explanation: The waterfall rule: always from strictest to loosest, from highest to lowest.
Which formula will bring the product's category to every row in the Sales table?
explanation: RELATED takes a specific column from the 1 side of the relationship, not a table name.
Which operator concatenates Product[Model] andProduct[Color] to a single label?
Where is it recommended to centralize all measures in the model?
explanation: The measures don't belong to any data table — centralizing them brings order to the report.
You haven't answered questions yet — 0 out of 25
The family that lets a cell look at its neighbors
Chapter X · Summary and Self-Check
Window functions question bank — 22 questions
Questions about the window function family: what each function takes, in what order, and how it behaves. There are no questions here that require the data file.
What is the default direction in ORDERBY?
explanation: This is the most common issue: you write a sales measure, expect a ranking from largest to smallest, and get exactly the opposite. Always write DESC explicitly.
you wrote ORDERBY ( [Total Sales] ) without a direction. Which country will get position 1?
explanation: The default ASC sorts from smallest to largest, and Germany is the smallest with $4,878,300.
Why is it required [Total Sales] Inside ORDERBY and not SUM ( … ) raw?
explanation: Without a context transition, all countries get the same sort value — and they all end up in the same place.
What does ORDERBY do to the list of contenders?
explanation: The filtering happens somewhere else entirely. ORDERBY is only an ordering decision.
What happens if two countries end up in an exact tie in ROWNUMBER?
explanation: So on a tie, add a second sort key, or switch to RANK, which knows how to share a rank.
Two countries are tied for 2nd place. What will RANK return for the next country after them by default?
explanation: The default is SKIP. With DENSE it would have received 3.
Which parameter changes the tie behavior to DENSE in RANK?
explanation: In RANK it's the first parameter; in the older RANKX it's actually the fifth parameter.
Why is ALL required inside the window function?
explanation: Without ALL there's nothing to number: the cell's bubble contains only itself.
In PARTITIONBY on Group — where must the Group column also appear?
explanation: This is one of the two golden rules of PARTITIONBY.
The visual shows only Country, and the measure uses PARTITIONBY on Group. What will happen?
explanation: The visual must show both levels in rows — both Group and Country.
with ORDERBY ( SalesTerritory[Country], ASC ) — which country gets 1?
explanation: Alphabetical sort. The exact same function, just a different sort column — and all the numbers change.
what will show Row By Sales In the Canada row?
explanation: Canada is second in the ranking by sales, after the USA.
What will appear in the Total row of a ROWNUMBER measure?
explanation: This is the blank rule. It's best to simply turn off Totals on the visual.
What it returns INDEX ( 1, … )?
explanation: INDEX flips the direction of the question: not "where am I" but "who's in position N".
Why wrap INDEX in CALCULATE?
explanation: On its own INDEX just returns a row; CALCULATE is what moves the calculation there.
How do you retrieve the last row of the ordering?
explanation: In Europe, -1 with PARTITIONBY on Group returns Germany ($4,878,300).
what does OFFSET ( -1, … ) in a descending sort row?
explanation: This is the function of "how much do I need to pass the next one in line" — comparison to a neighbor, not to the champion.
what will show One Above Sales in the United States row?
explanation: This is the edge of the window, not a glitch. Say this to the class before someone asks.
What the join produces WINDOW ( 1, ABS, 0, REL, … )?
explanation: An absolute start at the top of the list, a relative end at mine — and the window grows row by row.
What must appear in the last row of a valid Running Total measure?
explanation: This is a built-in sanity check: the window ends on the last row so it covers the whole list.
why Top 3 Sales Shows the same number in every row?
explanation: One word — REL instead of ABS — turns a fixed window into a sliding window.
Which behavior is correct regarding RANKX vs. RANK in the Total row?
explanation: RANKX compares values so it works at any level; RANK identifies a row, so it requires an identified row.
You haven't answered questions yet — 0 out of 22
The last two tasks are window functions
Chapter X · Summary and Self-Check
Advanced DAX Question Bank — 29 Questions
Twenty-nine questions about the twenty advanced functions — syntax, argument order, and behavior. There are no questions here that require the data file.
What is the behavior of KEEPFILTERS inside CALCULATE?
explanation: The default behavior of CALCULATE is to override: a condition on a column drops any existing filter on that same column.
and the same red slicer, but with KEEPFILTERS ( Product[Color] = "Blue" )?
explanation: A product can't be both red and blue, so the intersection is empty.
What is the main difference between ALL and ALLSELECTED?
explanation: ALLSELECTED removes only the row-level filter — which is why it's the function for "share of the selection".
The user selected only Bikes and Clothing in the slicer. Which measure will show 100% on the combined Bikes+Clothing row?
explanation: The denominator of ALL stays $109,809,274 regardless of the selection; the denominator of ALLSELECTED shrinks to the two selected categories.
What it returns CALCULATE ( [Total Sales], ALLEXCEPT ( SalesTerritory, SalesTerritory[Group] ) ) In the country row?
explanation: ALLEXCEPT clears everything except Group, so every row gets its group's total.
What's the necessary condition for using USERELATIONSHIP?
explanation: The function doesn't create a new relationship — it only activates a relationship already defined in the model, for the duration of the calculation.
Why can't USERELATIONSHIP be demonstrated on the model we built?
explanation: To try it out you need to add another relationship, for example from ShipDateKey, and leave it inactive.
what does CROSSFILTER ( … , … , BOTH )?
explanation: This is the measured alternative to global bidirectional filtering, which we learned in lesson 2.
and what does CROSSFILTER ( … , … , NONE )?
explanation: Useful when you want an "unfiltered" value from a related table without touching the model.
What is unique about TREATAS?
explanation: This is the way to connect a targets table or a parameters table to the model without building a relationship.
Why is SUMMARIZECOLUMNS preferable to SUMMARIZE with calculated columns?
explanation: SUMMARIZE with calculated columns inside it is considered a bad pattern today.
Where does SUMMARIZECOLUMNS run?
explanation: It's not written as a regular measure; it's a query function.
What does ROLLUPADDISSUBTOTAL add to the result?
explanation: It's written inside SUMMARIZECOLUMNS and generates Subtotals and Totals automatically.
Which measure, broken down from window functions, returns exactly the same result?
explanation: Two completely different functions that arrive at the same number — the best sanity check there is.
Places 3 and 4 are exactly tied. How many rows will TOPN ( 3, … )?
explanation: When ranking products by quantities this happens all the time, so it's important to know this behavior.
What it returns EXCEPT ( VALUES ( SalesTerritory[Country] ), { "United States" } )?
explanation: This is a set operation: what's in the first table and not in the second, without duplicates.
Which business question does EXCEPT solve most naturally?
explanation: This is the classic Churn pattern: EXCEPT between two VALUES lists in different time contexts.
What's special about GENERATE compared to a regular cross join?
explanation: This is what enables patterns like "the top three products in every category".
What does ISINSCOPE return?
explanation: This is the tool for writing a measure that behaves differently in a detail row, a summary row, and a Total row.
In a matrix of Group over Country — what will ISINSCOPE ( SalesTerritory[Country] ) in the Europe summary row?
explanation: and in that same row [Total Sales] will show $19,800,577 — Europe's total.
The user selected two countries in the slicer. What will SELECTEDVALUE ( SalesTerritory[Country], "All Countries" )?
explanation: The function returns a value only when the context is narrowed to a single value.
Why isn't USERPRINCIPALNAME actually checked in Power BI Desktop?
explanation: You test it with View as roles — there you simulate a role and see the real result.
what does COALESCE ( [Total Sales], 0 )?
explanation: The advantage over IF ( ISBLANK ( … ) ) is that the expression is evaluated only once.
Why COMBINEVALUES is safer than concatenation with & to create a composite key?
explanation: That's why it's the correct choice for composite keys in RLS and in virtual relationships across several columns.
What's the connection between CONTAINSROW and the IN operator?
explanation: The power of CONTAINSROW begins when you check a combination of several columns together — something IN can't do.
What is the role of EARLIER?
explanation: The common mistake is to think it is "the previous row" — it is not. It is the outer context.
What actually replaced EARLIER in modern DAX code?
explanation: You write VAR CurrentCat = Product[Category] and use the variable. It's still important to know EARLIER, because it's in every piece of code written before 2015.
What does DATATABLE allow?
explanation: This is the code alternative to Enter Data that we used for the Measures table.
Which join builds a "percent of target met" measure without a physical relationship in the model?
explanation: DATATABLE creates the targets table, TREATAS links it to the categories, and DIVIDE calculates the percentage.
You haven't answered questions yet — 0 out of 29
Syntax, argument order, parentheses and commas
Chapter X · Summary and Self-Check
Syntax bank — 90 questions
Ninety questions, and all of them about syntax: the order of the arguments, parentheses, commas, and what is valid and what is not. There are no questions here about results — only about how the code is written.
group
How many questions
about what
Aggregation and references
10
SUM · COUNTROWS · DISTINCTCOUNT · DIVIDE · measure parentheses vs. column
logic
8
IF · SWITCH · TRUE () · && · || · IN
Row context and iterators
8
RELATED · RELATEDTABLE · SUMX and the X family · EARLIER
CALCULATE and filters
12
Argument order · ALL · FILTER · KEEPFILTERS · ALLEXCEPT · VALUES
TREATAS · USERELATIONSHIP · CROSSFILTER · SUMMARIZECOLUMNS · TOPN · PATH · DATATABLE and more
M language
5
let … in · commas between steps · each · Case sensitivity
Why exactly syntax: Most DAX errors are not thinking errors but ordering errors - a parameter in the wrong place, a missing parenthesis, or an extra comma. The questions here are built exactly around the points where this happens.
Which way of writing SUM is valid?
The correct syntax: SUM accepts a single argument in the format Table[Column]. The table name is required.
We want to sum quantity × price. Which line is syntactically valid?
The correct syntax: SUM accepts only a column — not an expression and not two arguments. For an expression within a row you need an iterator: SUMX ( table, expression ).
What argument does COUNTROWS accept?
The correct syntax: COUNTROWS is the only one in the counting family that takes table, or an expression that returns a table.
Which way of writing DISTINCTCOUNT is valid?
The correct syntax: Just one column. To count a combination of several columns you need COUNTROWS ( SUMMARIZE ( … ) ).
What is the correct argument order in DIVIDE?
The correct syntax: The numerator first, the denominator second, and the alternate value — optional — third.
You want DIVIDE to return 0 instead of blank when the denominator is zero. How do you write it?
The correct syntax: The third argument is the value returned when the denominator is 0 or blank.
Which notation indicates measure and not a column?
The correct syntax: Square brackets without A table name before them = measure. With a table name = column.
How do you reference a column in a table whose name contains a space, e.g. Sales Territory?
The correct syntax: A table name with a space or special character is wrapped in single quotes. Without a space — it's not required.
Which function will count non-empty cells in a column text?
The correct syntax: COUNT is intended for numbers and dates. For text there's COUNTA.
Which measure-name definition is valid?
The correct syntax: Measure names in DAX allow characters like % and a hyphen. What matters is that the name is unique and that references to it use square brackets.
What is the syntax of IF?
The correct syntax: the first condition. The third argument is optional — the square brackets in the documentation mark exactly that.
You omitted the third argument in IF. What's syntactically correct?
The correct syntax: The third argument is optional; rows that didn't meet the condition get Blank.
Which way of writing SWITCH with logical conditions is valid?
The correct syntax: The trick is TRUE () — with parentheses — as the first argument. So every "value" turns into a free logical condition.
How is the last argument of SWITCH written — the default?
The correct syntax: value ← result pairs along the list, and a single value at the end — that's the default.
What is the logical AND operator in DAX?
The correct syntax:&& is AND, whereas & The lone one is text concatenation. There's also a function AND ( a, b ) But it's limited to two arguments.
What is the logical OR operator in DAX?
The correct syntax: two vertical bars. The function OR ( a, b ) It also exists, but is limited to two arguments.
Which way of writing an IN condition is valid?
The correct syntax: A list of values in DAX is written in curly braces — that's an anonymous table.
What the sign indicates <> in DAX?
The correct syntax: for example Product[Category] <> "Bikes" — everything that is not bikes.
Which way of writing RELATED is valid?
The correct syntax: RELATED accepts A specific column on the 1 side of the relationship — not a table name.
You want all the sales rows of the current product, from a calculated column in Product. What fits?
The correct syntax: RELATED returns a single value from the 1 side; RELATEDTABLE returns a table from the * side.
What is the syntax of SUMX?
The correct syntax: The table that runs first, and the expression computed on each row second.
Which of the options is not an iterator with the same syntax as SUMX?
The correct syntax: DISTINCTCOUNT takes a single column. The entire X family takes ( table, expression ).
Which calculated column definition is valid in the Sales table?
The correct syntax: In a calculated column, refer to columns with the table name, without aggregation functions. The sign := Belongs to Tabular Editor, not Power BI Desktop.
Which operator concatenates two text values?
The correct syntax: for example Product[Model] & " · " & Product[Color].
What is the syntax of EARLIER?
The correct syntax: Takes a column, and an optional second parameter that says how many levels to go up through the nested row contexts.
Where is RELATED allowed to be used?
The correct syntax: Without Row Context there's no "current row" to jump from through the relationship.
What is the argument order in CALCULATE?
The correct syntax: The expression always comes first; followed by zero or more filters.
What does the comma between two filters inside CALCULATE mean?
The correct syntax: The comma is an automatic AND; there's no need to write it explicitly.
Which boolean condition inside CALCULATE is valid?
The correct syntax: Inside CALCULATE you must identify a column in the format Table[Column]. Parentheses alone are read as a measure.
What's the syntax difference between ALL ( Product ) toALL ( Product[Color] )?
The correct syntax: ALL accepts a whole table, one column, or several columns from the same table.
Which syntax of ALL with several columns is valid?
The correct syntax: several columns separated by a comma within the same call — as long as they're all from the same table.
What is the syntax of FILTER?
The correct syntax: the table you iterate over first, and the logical condition second.
FILTER returns a table. Where must it be placed to filter a measure?
The correct syntax: By itself it just returns a table; CALCULATE is what applies it as a filter.
What's the correct way to combine ALL inside FILTER?
The correct syntax: The ALL sets the list of candidates, and FILTER picks from within it. This order is the pattern.
How do we wrap a boolean condition in KEEPFILTERS?
The correct syntax: KEEPFILTERS wraps the the filter, not the calculated expression.
What is the syntax of ALLEXCEPT?
The correct syntax: The table comes first — it's the one that gets cleaned — followed by the columns that stay filtered.
What is the valid syntax for ALLSELECTED?
The correct syntax: Without arguments it operates on the visual's entire context.
What it returns VALUES ( SalesTerritory[Country] )?
The correct syntax: and so it can be put into EXCEPT, FILTER, or COUNTROWS.
Where is ORDERBY written?
The correct syntax: ORDERBY never stands on its own — it is always an argument inside a window function.
What is the syntax of ORDERBY?
The correct syntax: The expression first, a comma, and then the direction keyword — ASC or DESC.
you wrote ORDERBY ( [Total Sales] ) Without a direction keyword. What's correct?
The correct syntax: The direction is optional, so the mistake is silent: you get ascending sort instead of descending.
What is the syntax of PARTITIONBY?
The correct syntax: Just a list of columns, no direction and no ALL. Direction belongs to ORDERBY.
The PARTITIONBY split column must also appear in…
The correct syntax: It's part of the list of contenders, so: ALL ( SalesTerritory[Country], SalesTerritory[Group] ).
What is the argument order in ROWNUMBER?
The correct syntax: The list comes first, then ORDERBY, and last — the optional PARTITIONBY.
Where is the DENSE parameter written in RANK?
The correct syntax: In RANK the tie-breaker is the parameter the first. The default is SKIP.
What is the minimal syntax of RANK?
The correct syntax: A list of contenders + ORDERBY. Without ORDERBY there's nothing to rank by.
What is the minimal syntax of RANKX?
The correct syntax: In RANKX the table comes first and the expression second — the opposite of what RANK intuition suggests, and without ORDERBY at all.
Where is the tiebreaker written in RANKX?
The correct syntax: This is exactly the syntactic difference from RANK, where it comes first.
What is the syntax of INDEX?
The correct syntax: The requested position is the first argument, followed by the list and the ORDERBY.
How do you retrieve the last row with INDEX?
The correct syntax: A positive number counts from the start, a negative one from the end.
INDEX alone returns a row. How do you make a measure take that row into account?
The correct syntax: CALCULATE is what shifts the calculation to the fetched row.
What is the syntax of OFFSET?
The correct syntax: The distance is the first argument — exactly like the position in INDEX.
In a descending-order row, what value does the row above me return?
The correct syntax: A negative value is a step backward in the sort order; positive is a step forward.
What is the order of the first four arguments of WINDOW?
The correct syntax: A pair for each end: number followed by type. For example WINDOW ( 1, ABS, 0, REL, … ).
What do the words ABS and REL indicate?
The correct syntax:ABS = a fixed position in the list · REL = distance from the current row.
Which join writes a running total from the top of the list to the current row?
The correct syntax: An absolute start at position 1, and a relative end at distance 0 — meaning at me.
Which combination produces a fixed window of the first three rows?
The correct syntax: Two absolute points ← the window is the same for all rows ← a constant column.
Which join produces a window of "me and the neighbor above me"?
The correct syntax: Two relative edges: one step back and up to me.
Which way of writing the partition list in a window function is valid?
The correct syntax: The argument is a table expression. Without ALL, the Canada row has only one candidate.
Why is it required [Total Sales] and not SUM ( Sales[Sales Amount] ) Inside ORDERBY?
The correct syntax: Syntactically both forms are accepted; only one of them gives the correct result.
What is the syntax of TREATAS?
The correct syntax: The virtual table first, followed by the columns to which the filter will be applied — in order.
What is the syntax of USERELATIONSHIP?
The correct syntax: Two columns — the two ends of the relationship. No direction argument.
Where must USERELATIONSHIP be written?
The correct syntax: It changes the context, so it must sit where the context is changed.
What is the syntax of CROSSFILTER?
The correct syntax: two columns and then the direction word — the direction is the third and final argument.
What direction keywords does CROSSFILTER accept?
The correct syntax: ASC/DESC belong to ORDERBY, ABS/REL to WINDOW, and SKIP/DENSE to RANK.
What is the argument order in SUMMARIZECOLUMNS?
The correct syntax: The grouping columns, followed by optional filters, and finally pairs of name and content.
How is a calculated column name written inside SUMMARIZECOLUMNS?
The correct syntax: A pair: a string in double quotes, a comma, then the expression.
Which keyword opens a DAX query?
The correct syntax:EVALUATE opens a query that returns a table, and after it you can ORDER BY.
Where is ROLLUPADDISSUBTOTAL written?
The correct syntax: It wraps the grouping columns and adds summary rows to them.
What is the syntax of TOPN?
The correct syntax: The number first, then the table, then optional pairs of a sort expression and direction.
Which direction keyword does TOPN accept?
The correct syntax: for example TOPN ( 3, ALL ( SalesTerritory[Country] ), [Total Sales], DESC ).
What is the syntax of EXCEPT?
The correct syntax: Two tables with the same number of columns; the rows that are in the first but not the second are returned.
What is the syntax of GENERATE?
The correct syntax: Two tables. The second is re-evaluated for every row in the first.
What is the argument of ISINSCOPE?
The correct syntax: for example ISINSCOPE ( SalesTerritory[Country] ) — and returns TRUE or FALSE.
What is the syntax of SELECTEDVALUE?
The correct syntax: the column is first; the fallback value is a second, optional argument.
What is the syntax of COALESCE?
The correct syntax: A list of expressions; the first non-blank one is returned.
What is the syntax of COMBINEVALUES?
The correct syntax: The delimiter is the argument the first — contrary to Excel intuition.
What is the syntax of CONTAINSROW?
The correct syntax: The table first, then the values — one for each column in the table.
What is the syntax of PATH?
The correct syntax: The record's ID first, and the parent ID second. The reverse order will give a wrong result.
What is the syntax of PATHITEM?
The correct syntax: the track first, the position second, and the type — TEXT or INTEGER — optional at the end.
What is the syntax of USERPRINCIPALNAME?
The correct syntax: The empty parentheses are mandatory — exactly like inTRUE () and inTODAY ().
How are rows written inside DATATABLE?
The correct syntax: An outer closer for the whole set of rows, and an inner closer for each row separately.
How are the columns defined in DATATABLE?
The correct syntax: Pairs of a quoted name and a data type, alternating, before the rows block.
What is the structure of an M query?
The correct syntax: between let toin the steps are defined, and after in Indicate which step is the output.
What separates steps in M?
The correct syntax: One last step before in written without a comma — a very common error.
Which syntax is valid in M?
The correct syntax: M is Case Sensitive: a misspelled function name will throw an error. DAX, by contrast, is not case sensitive.
What is the role of the word each in M?
The correct syntax: This is syntactic shorthand for a function with a single parameter. Inside it, refer to columns using square brackets only.
How is a list of column names written inTable.SelectColumns?
The correct syntax: A list in M is written in curly braces, and names in double quotes.
What is the correct syntax for a variable in DAX?
The correct syntax:VAR to the definition andRETURN for the output. You can have several VARs in a row, but only one RETURN — and it's always last. let … in belongs to M.
How do you write a comment in DAX?
The correct syntax: The three shapes //, -- and/* … */ supported; # is not supported.
You haven't answered questions yet — 0 out of 90
and now write it yourselves in Power BI
Chapter X · Summary and Self-Check
Code and errors — 40 syntax questions
Forty questions, and each one opens withA real code snippet. The question is always the same question: what's wrong here, what's missing, and what's the correct order.
What you're tested on
examples
Missing or extra parentheses
An unclosed bracket · double curly braces in M
commas
Missing comma between arguments · semicolon instead of comma · trailing comma at the end of a step in M
Table[Column] against [measure] · Single quote vs. double quotes
Operators
>= against => · <> against != · & against +
structure
VAR … RETURN · let … in · TRUE () · notes
Every code snippet has a copy button. You can copy the faulty line, paste it into Power BI, and see the error with your own eyes — that's the best way to remember it.
What's wrong with this code?Total Sales = SUM ( Sales[Sales Amount]
The correct syntax: Every opening bracket must have a closing bracket. The correct syntax: SUM ( Sales[Sales Amount] ).
Why will this row fail?Total Sales = SUM ( Sales, Sales[Sales Amount] )
The correct syntax: Only the iterators (SUMX and its siblings) receive ( table, expression ). SUM takes one column.
What's missing in the row?Margin % = DIVIDE ( [Total Profit] [Total Sales] )
The correct syntax: Arguments are always separated by a comma: DIVIDE ( [Total Profit], [Total Sales] ).
What's the problem here?Bikes = CALCULATE ( [Total Sales]; Product[Category] = "Bikes" )
The correct syntax: DAX separates arguments with a comma only. The semicolon belongs to other languages.
What's wrong with the condition?Bikes = CALCULATE ( [Total Sales], Product[Category] = 'Bikes' )
The correct syntax: A single quote is reserved for table names with a space, for example 'Sales Territory'. String = double quotes.
Which of the rows is syntactically valid?
The correct syntax: A table name may be wrapped in single quotes. Double quotes around a table name, or a column without a table — an error.
What is wrong in the list of values?Apparel = CALCULATE ( [Total Sales], Product[Category] IN ( "Clothing", "Accessories" ) )
The correct syntax: The correct spelling: IN { "Clothing", "Accessories" } — the curly braces create an anonymous table.
What is missing in the call to FILTER?Premium = FILTER ( Product[List Price] > 3000 )
The correct syntax: The syntax is FILTER ( table, condition ) — for example FILTER ( Product, Product[List Price] > 3000 ).
What will happen with this row?x = CALCULATE ( [Total Sales], FILTER ( Product, [List Price] > 3000 ) )
The correct syntax: Inside FILTER you must Product[List Price]. Square brackets alone = a reference to a measure.
How many closing round brackets are missing?x = CALCULATE ( [Total Sales], FILTER ( ALL ( Product ), Product[List Price] > 3000
The correct syntax: Three functions were opened — CALCULATE, FILTER, and ALL — and none was closed.
The correct syntax: A function name in M must be written with the exact letter casing. In DAX, by contrast, there is no case sensitivity.
What's wrong inside the each?Table.SelectRows ( Source, each Sales[Order Quantity] > 0 )
The correct syntax: The correct spelling: each [Order Quantity] > 0. The prefix Table[Column] belongs to DAX.
Which type of brackets creates list in M?
The correct syntax: In M: curly braces = list · square brackets = record · round brackets = function arguments.
Why are there double curly braces here?Table.TransformColumnTypes ( Source, { { "Order Quantity", Int64.Type } } )
The correct syntax: To convert two columns: { {"A", type text}, {"B", Int64.Type} }.
What's wrong with the argument order?Table.AddColumn ( "Profit", Source, each [Sales Amount] - [Total Product Cost] )
The correct syntax: The correct spelling: Table.AddColumn ( Source, "Profit", each … ).
Which comment line is valid in DAX?
The correct syntax: DAX supports//, in-- and in/* … */. The hash sign is not supported.
You haven't answered questions yet — 0 out of 40
and now write it yourselves in Power BI
Chapter XI · Hands-on Practice
Homework — 12 tasks with solutions
All the tasks in Power BI Desktop on AdventureWorks. After every task there's a full solution with the number you should get. The last two tasks are window functions.
Before we begin
AdventureWorks_Sales.xlsx
Make sure the model is connected (lesson 2) and the types are clean (lesson 3). All measures here are written in the Data pane.
DAX function guide — 122 functions: syntax, every argument, an example, and pitfalls
All the course's questions in one place — 363 multiple-choice questions from all four lessons, with filtering by bank, shuffling, and tracking what you got wrong
The model now speaks the language of numbers
You've finished lesson 4 — and with it, the core of DAX. Now it's time to practice: 205 questions across five banks, and 12 tasks on the file itself.
You can also navigate with the buttons at the top of the screen, keyboard arrows, and on phone with a swipe