These DAX practice exercises are designed to make you write the measure before seeing the answer. They move from base measures to filter context, time intelligence, and totals, using one small star schema throughout.
For each exercise:
- write down the expected behavior;
- create the measure without opening the solution;
- test it at row and grand-total level; and
- explain why each filter is still active or has been changed.
If you only compare your syntax with the answer, you miss the most important part of the exercise.
The practice model
Assume a standard one-to-many star schema:
Salesis the fact table at order-line grain.ProductfiltersSalesthroughProductKey.CustomerfiltersSalesthroughCustomerKey.'Date'filtersSalesthroughOrder Date.
The exercises use these columns:
Sales[Order Number]
Sales[Order Date]
Sales[ProductKey]
Sales[CustomerKey]
Sales[Quantity]
Sales[Sales Amount]
Sales[Cost Amount]
Product[ProductKey]
Product[Category]
Product[Color]
Customer[CustomerKey]
Customer[Country]
'Date'[Date]
'Date'[Year]
'Date'[Month]
If your model uses different names, map the columns by meaning rather than copying the formulas blindly.
Exercise 1: Create a total sales measure
Task: Return the sum of Sales[Sales Amount]. It should respond to every filter that reaches the Sales table.
Before checking the solution, place the measure in a matrix by Product Category and predict the grand total.
Solution
Total Sales =
SUM ( Sales[Sales Amount] )
This is a base measure: small, reusable, and intentionally unaware of any particular visual. Product, Customer, and Date filters change the rows visible to SUM.
Exercise 2: Count orders, not order lines
Task: Return the number of unique orders.
The Sales table is at order-line grain, so COUNTROWS(Sales) would count lines rather than orders.
Solution
Order Count =
DISTINCTCOUNT ( Sales[Order Number] )
Test an order that contains several products. The order should contribute one to the count, not one per line.
The lesson is broader than DISTINCTCOUNT: identify the business grain before choosing an aggregation.
Exercise 3: Calculate average order value safely
Task: Divide total sales by order count. Return blank rather than an error when no orders exist.
Solution
Average Order Value =
DIVIDE ( [Total Sales], [Order Count] )
DIVIDE handles a zero or blank denominator. More importantly, this measure divides two values evaluated under the same filter context.
Do not replace it with AVERAGE(Sales[Sales Amount]); that would calculate the average order-line amount, which is a different question.
Exercise 4: Calculate sales for blue products
Task: Return sales for products whose color is Blue, while preserving Date and Customer filters.
Solution
Blue Sales =
CALCULATE (
[Total Sales],
Product[Color] = "Blue"
)
CALCULATE reevaluates [Total Sales] after applying a filter to Product Color. Date and Customer filters remain active.
Now place Product Color on rows. On a Red row, the filter argument replaces the existing Red filter with Blue. If you wanted the new condition to intersect with the existing selection, you would need a different filter behavior, such as KEEPFILTERS.
Exercise 5: Calculate percent of all product sales
Task: Show each Product Category’s share of sales while keeping filters from Date, Customer, and other dimensions.
Solution
Sales All Products =
CALCULATE (
[Total Sales],
REMOVEFILTERS ( Product )
)
Product Sales Share =
DIVIDE ( [Total Sales], [Sales All Products] )
The denominator removes filters from the Product table only. If the user selects Year 2026 and Country = Canada, those filters still affect the denominator.
This is usually more precise than clearing filters from the entire Sales table.
Exercise 6: Calculate gross margin percentage
Task: Return gross profit divided by sales. The total should be calculated from total profit and total sales—not as the average of the visible row percentages.
Solution
Total Cost =
SUM ( Sales[Cost Amount] )
Gross Profit =
[Total Sales] - [Total Cost]
Gross Margin % =
DIVIDE ( [Gross Profit], [Total Sales] )
At the total, Power BI reevaluates all three measures in the total filter context. That produces a weighted business total.
An AVERAGEX over visible category percentages would answer a different question: the unweighted average category margin.
Exercise 7: Calculate year-to-date sales
Task: Accumulate Total Sales from the beginning of the year through the current date context.
Solution
Sales YTD =
CALCULATE (
[Total Sales],
DATESYTD ( 'Date'[Date] )
)
This requires a valid Date table with a continuous date column and a working relationship to Sales[Order Date]. See Microsoft’s DATESYTD reference for the supported calendar and year-end forms.
Test the measure by month and by individual date. If the model uses a fiscal year, supply the appropriate year-end argument or use a calendar design that matches the business definition.
Exercise 8: Compare sales with the previous year
Task: Return sales for the corresponding period one year earlier, then calculate year-over-year growth.
Solution
Sales Previous Year =
CALCULATE (
[Total Sales],
SAMEPERIODLASTYEAR ( 'Date'[Date] )
)
Sales YoY % =
DIVIDE (
[Total Sales] - [Sales Previous Year],
[Sales Previous Year]
)
Test a complete month and a partial month. “Previous year” is only meaningful when the current date context is understood. Microsoft documents the function’s calendar behavior in the SAMEPERIODLASTYEAR reference.
Exercise 9: Return the selected product color
Task: Display the selected Product Color when exactly one distinct color is visible. Otherwise, display All or multiple colors.
Solution
Selected Color =
SELECTEDVALUE (
Product[Color],
"All or multiple colors"
)
SELECTEDVALUE returns a value only when the column has one distinct value in the current filter context. With zero or multiple values, it returns the alternate result.
If your measure unexpectedly returns blank, work through the seven SELECTEDVALUE checks rather than adding random filters.
Exercise 10: Sum only products above a threshold
Task: Evaluate Total Sales once per product. Include a product’s sales only when that product has at least 10,000 in sales, then add the qualifying products.
Solution
Sales From Large Products =
SUMX (
VALUES ( Product[ProductKey] ),
IF (
[Total Sales] >= 10000,
[Total Sales],
0
)
)
The iterator establishes product as the evaluation grain. During each iteration, the measure reference causes the current product row to affect the filter context used by [Total Sales].
If you iterate Product Category instead, you change the business rule to “include categories above 10,000.” Both formulas may be valid DAX, but they do not answer the same question.
A bonus debugging exercise
Take any one of the ten solutions and make one deliberate mistake:
- count rows instead of orders;
- remove every filter instead of Product filters;
- use an average where the business needs a weighted ratio;
- iterate categories instead of products; or
- evaluate a slicer-dependent expression in a calculated column.
Then diagnose it using this sequence:
- reproduce the failure in the smallest possible visual;
- display the intermediate measures;
- list the values visible in the current filter context;
- test the row, subtotal, and total independently; and
- change one assumption at a time.
That method is covered in more detail in 5 DAX debugging habits that find wrong totals faster.
How to get more from these DAX exercises
Redo the set three times:
- Round 1: use the hints and focus on correctness;
- Round 2: solve without hints and explain the context;
- Round 3: change the model or business rule and adapt the measure.
For example, change “Blue Sales” to “Blue or Black Sales,” change the product threshold, or calculate share within Category rather than across all Products.
If you want an organized learning sequence around these exercises, follow the 30-day plan to master DAX. For interactive scenarios with immediate feedback, continue with a DAX Solver challenge.