Intermediate Power BI (session 2)
Power BI
intermediate
Intermediate Power BI course
DAX
This is part of our intermediate-level Power BI desktop course. The course structure is as follows:
- session 1: a simple build-a-dashboard session as a warmup, reminder about core Power BI techniques, and sharing some suggestions for good workflow practices
- session 2 (this session): DAX language foundations, including FILTER and CALCULATE
- session 3: core PowerQuery functions to manage sources, join data sets, pivot, and tidy values
- session 4: PowerQuery M language foundations, including a basic overview of syntax, a core function toolkit, advanced mode, and reproducibility
- session 5: a second, more ambitious, build-a-dashboard session to put all that PowerQuery and DAX knowledge into practice
Session materials
Previous attendees have said…
- 29 previous attendees have left feedback
- 97% would recommend this session to a colleague
- 97% said that this session was pitched correctly

Three random comments from previous attendees
- very useful, thank you
- Definitely a difficult session, handled well though
- I could follow the session, but found it a bit confusing getting SUM and SUMX to do the same things, then adding in the CALCULATE with SUMX and ALL to do what SUM was doing. I understand that at some point you may want to do really complicated things that those sort of skills would come in handy, but to me that felt a bit more like “advanced” rather than “intermediate”. I understand the value of SUM and SUMX when SUMX is a subset, I can see how that would be really useful.
Forthcoming session(s)
| Booking link | Date |
|---|---|
| Intermediate Power BI (session 2) | 13:00-15:00 Thu 22nd January 2026 |
| Intermediate Power BI (session 2) | 10:00-12:00 Tue 5th May 2026 |
Session outline
In this session, we’ll concentrate on DAX. Specifically, we’ll:
- look in more detail at date tables as a warmup
- look at the foundations of DAX, especially concentrating on the differences between measures and tables
- discuss iterator functions
- think about the filter context, and the ways you can use (and modify) it with
FILTER()andCALCULATE() - introduce variables as a way of simplifying some DAX expressions
- and finish with a brief introduction to calculation groups as a means of reducing the number of DAX measures you need to write
Data
- this session uses a bit of synthetic data extending the Dundee traffic dataset from the previous session
- please use this link to import into Power BI. You should be able to do that directly from the Excel connector.
DAX reminders
- functions vs formulas
- arguments and return values
- references
- columns vs measures
A warmup: date tables
We briefly discussed and implemented a date table in the previous session. Date tables are good practice, because they help you deal with patchy data (especially when you have missing dates), and prevent repetition by consolidating all your date info into one place.
Task
- create a
datestable now using theMINandMAXvalues fromtraffic[date]- note that
dateis a reserved keyword, so probably better avoided as a table name
- note that
- now add some columns:
- a day of the week, using
WEEKDAY - a day name, using
FORMAT - a quarter, using
QUARTER
- a day of the week, using
- finally, check the model to ensure that your date table is linked to your traffic table
Column functions
- Many DAX functions can return either columns or measures
- let’s try a practical example:
Task
- in the
trafficdata, selectNew columnthen add the DAX formulatemp_col = 66 - now repeat with
New measurethentemp_measure = 66 - now add
temp_measureto a card on a new report page
Column vs measure
- your
temp_xxxfunctions will be identical: the value oftemp_measurewill be the same as each row of thetemp_colin your data - the values are the same
- the filter context is also the same
DAX defaults
Task
- now tweak your
temp_colformula to use theUPPERfunction on the[Detail]column:temp = UPPER(traffic[Detail]) - inspect the result
- an important result: DAX column functions default to row-wise filter context
- if you try the same formula in
temp_measureyou’ll cause an error: Power BI won’t know which row you want to change the case of
- this indicates a different filter context at play in this DAX formula when used as either a column or as a measure
FILTER()
Task
- now update
temp_col = COUNTROWS(filter(traffic, traffic[number] = 3))
- note that this function now isn’t working row-by-row, but across the entire table
-
FILTER()has changed the default row-by-row context
Filter context
Task
- to illustrate, please build a simple dashboard with:
- a card containing the minimum value of
traffic[temp_col] - a slicer containing
traffic[time]
- a card containing the minimum value of
- note that the value of temp seems to be incorrect - it’s the same no matter how the slicer is set
Iterator functions
Task
- add two new columns:
col_sum = SUM(traffic[number])col_sumx = SUMX(traffic, traffic[number])
- add two new measures
meas_sum = SUM(traffic[number])meas_sumx = SUMX(traffic, traffic[number])
- finally, add all 4 to cards, summarising by
minwhen necessary
What’s the difference?
- in the measure,
SUM()takes the report filter context, and sums for that time of day - in the column,
SUM()calculates the total for an entire column - in both cases,
SUMX()gives the same result, but it calculates it in a different way…
SUMX()
Task
- to illustrate, change both
SUMXcalculations toSUMX(filter(traffic, traffic[number] < 15), traffic[number])
- all four values will now disagree:

-
col_sumis showing the total for the entire column -
meas_sumis showing the total for the filtered column (sotraffic[time]= morning) -
col_sumxis showing the total for the filtered column (values oftraffic[number]> 15 only) -
meas_sumxis showing the total for the doubly-filtered column (values oftraffic[number]> 15 wheretraffic[time]= morning)
-
SUMX() is an iterator
-
SUMX()calculates row-by-row - in our initial case, it was calculating row-by-row, and then aggregating all the rows
- when we added a filter,
SUMX()included rows meeting the filter condition
ALL()
- strictly,
SUM(x[y])is a sweetened form ofSUMX(ALL(x), x[y]) -
ALL()removes any filter context to include every row of a table
ALL() and CALCULATE()
- much of the power of DAX lies in that ability to alter the filter context. Let’s tweak
meas_sumto show that:
Task
meas_sum = CALCULATE(SUM(traffic[number]), ALL())- compare and contrast col_sum and meas_sum
-
CALCULATE()is a generic function that changes the filter context -
ALL(), as we’ve already seen, removes any filtering from the current filter context
ALLEXCEPT()
-
ALLEXCEPT()allows you to remove part of the filter context
Task
- add an additional slicer to your report slicing on
traffic[Type] - add a new measure with
meas_sum_time = CALCULATE(SUM(traffic[number]), ALLEXCEPT(traffic, traffic[time]))and add to a card
- that should give you a measure that reponds to
time, but not toType - this could be extended - e.g. to
meas_sum_type_time = CALCULATE(SUM(traffic[number]), ALLEXCEPT (traffic, traffic[Type], traffic[time]))
Variables
- DAX formulas get horrid quickly. In this section, we’ll demonstrate the use of variables
- add a new measure:
temp_var =
VAR my_var = 66
RETURN my_var
Variable considerations
- useful to avoid repeating yourself
- also useful for avoiding clutter (e.g. component measures)
- local scope - you can’t refer to variables in other expressions
- potentially improve performance
Calculation groups
- calculation groups are a way of applying functions to many measures at once
Task
- in the model view, select
Calculation group
- select
Yeswhen warned about implicit measures
- that will add a new section to your model
- select
Calculation itemin the Model sidebar
- add the formula
round_10 = ROUND(SELECTEDMEASURE(), -1)to the formula bar - back on the report page, add a slicer and populate with your Calculation group