TEC-DATA85-07 · Lesson 4 · 14 clock hours · lab
DAX: Calculated Columns, Measures, and Time Intelligence
Objective. Students write DAX measures using CALCULATE, FILTER, and time-intelligence functions (TOTALYTD, SAMEPERIODLASTYEAR) and correctly distinguish when to use a calculated column versus a measure.
⚠ Pending professional review
This lesson, its worked examples and its practice questions has not yet been verified by an industry-certified professional or by a certification or approval body. It is a study draft and may contain errors. Do not use it as instructions for work on a job site or on live equipment. Always follow your instructor, your supervising professional, and current official references and regulations.
VoTechWorks program approval and accreditation are still in progress. Credentials are issued only by the certifying bodies named in each program, after their own exams.
What Is DAX?
DAX (Data Analysis Expressions) is the formula language built into Power BI, Analysis Services, and Power Pivot. It looks similar to Excel formulas but works very differently because it operates on entire columns and tables, not individual cells. Mastering DAX is central to the Microsoft PL-300 exam and to building credible business dashboards.
Calculated Columns vs. Measures: The Core Distinction
This is the single most important concept in this lesson.
Calculated columns are computed row by row when the data model is loaded or refreshed. The result is stored in the table, consuming memory. Use a calculated column when you need a per-row value that you can slice, filter, or use in a relationship — for example, a full name column that concatenates first and last name, or a profit-margin bucket that categorizes each order.
Measures are computed on the fly in response to the filter context at the moment a visual renders. They are not stored as data; only the formula is stored. Use a measure whenever you need an aggregation — total sales, average order value, year-over-year growth — because the result must change depending on what filters are applied. Measures are the workhorse of professional Power BI models.
A common mistake is creating a calculated column for something that should be a measure. Calculated columns ignore the report filter context, so a "Total Sales" calculated column will return the same row-level value no matter what slicer is selected — not what you want.
The CALCULATE Function
`CALCULATE` is the most powerful function in DAX. It evaluates an expression in a modified filter context.
```
Sales in North = CALCULATE(SUM(Sales[Amount]), Sales[Region] = "North")
```
This returns the sum of Amount, but only for rows where Region equals "North," regardless of any other filter on the report. The first argument is the expression; every argument after that is a filter modifier. You can pass multiple filters, or use `FILTER`, `ALL`, `REMOVEFILTERS`, and similar functions as modifiers.
The FILTER Function
`FILTER` returns a table — every row of a table that meets a condition. It is often used inside `CALCULATE` when a simple equality filter is not enough:
```
High-Value Orders = CALCULATE(
COUNTROWS(Orders),
FILTER(Orders, Orders[OrderTotal] > 1000)
)
```
Because `FILTER` iterates every row, it can be slower on large tables than a simple filter argument. Use simple filter arguments whenever the condition allows it, and reserve `FILTER` for row-by-row comparisons or complex conditions.
Time Intelligence Functions
Time intelligence functions require a properly marked Date table in your model — a table that contains one row for every calendar date in your data range, with no gaps, marked as a Date table in Power BI. Without this, time intelligence will return blank or wrong results.
TOTALYTD returns the year-to-date total of an expression, resetting at the start of each fiscal or calendar year:
```
Revenue YTD = TOTALYTD(SUM(Sales[Amount]), Dates[Date])
```
SAMEPERIODLASTYEAR shifts the current date filter back exactly one year, letting you compare this period to the same period in the prior year:
```
Revenue LY = CALCULATE(SUM(Sales[Amount]), SAMEPERIODLASTYEAR(Dates[Date]))
```
Combine these to build a year-over-year variance measure:
```
YoY Variance % =
DIVIDE(
SUM(Sales[Amount]) - [Revenue LY],
[Revenue LY]
)
```
Using `DIVIDE` instead of the `/` operator prevents divide-by-zero errors; it returns BLANK (or an optional alternate result) when the denominator is zero.
Common DAX Errors and How to Read Them
- Circular dependency: A calculated column refers to another calculated column that refers back to the first. Break the chain by rewriting one column as a measure or combining the logic into a single formula.
- Blank measure: The formula evaluates but returns no value — often because a relationship is missing or broken, or because `CALCULATE` filters out all rows. Check the model diagram and confirm table relationships are active.
- Wrong context transition: Using a measure inside a calculated column without `CALCULATE` wrapping it. A calculated column has row context, not filter context; `CALCULATE` converts row context to filter context automatically when you call a measure.
- Missing relationship: A `RELATED` function or cross-table filter cannot resolve because no relationship exists between the two tables. Go to the Model view and draw the relationship.
Formatting and Professionalism
When displaying measures as card visuals, always set the format: add a thousands separator, choose the right number of decimal places, and add a unit label in the visual's callout value field. A KPI card showing "1234567" tells stakeholders nothing; "$1,234,567" is immediately readable. These formatting habits are tested in the PL-300 exam under the "Visualize and analyze the data" domain.
Workplace Safety Note
Data science work is primarily computer-based. Maintain ergonomic workstation setup — monitor at eye level, keyboard and mouse at elbow height — to prevent repetitive strain. Protect sensitive business datasets by following your organization's data governance policy: do not export client data to personal drives or unapproved cloud services. When working with shared Power BI workspaces, apply role-level security so users see only the data they are authorized to access.
Key terms10
DAX (Data Analysis Expressions)The formula language used in Power BI, Analysis Services, and Power Pivot to define calculations on columns, tables, and measures.
Calculated columnA column added to a table in the data model whose value is computed row by row at refresh time and stored in memory.
MeasureA DAX formula that is evaluated dynamically in response to the current filter context when a visual renders; results are not stored as data.
Filter contextThe set of filters currently applied to the data model — from slicers, report filters, visual interactions, and CALCULATE modifiers — that determines which rows a measure evaluates.
CALCULATEA DAX function that evaluates an expression after applying one or more filter modifications to the current filter context.
TOTALYTDA time intelligence function that returns the year-to-date cumulative total of an expression, resetting at the fiscal or calendar year boundary.
SAMEPERIODLASTYEARA time intelligence function that returns a table of dates shifted back exactly one year, used to compare current-period values to the prior-year equivalent.
DIVIDEA DAX function that performs division and returns BLANK (or a specified alternate value) instead of an error when the denominator is zero.
Context transitionThe automatic conversion of row context into an equivalent filter context that occurs when a measure is called inside a calculated column or an iterator function wrapped in CALCULATE.
Date tableA table containing one row for every calendar date in the data range, with no gaps, that must be marked as a Date table in Power BI before time intelligence functions will work correctly.
Worked examples4
1. Write a measure that calculates total revenue only for the product category 'Electronics', regardless of any category filter already on the report.
- Step 1 — Identify the aggregation needed: we want SUM of the Sales[Amount] column.
- Step 2 — Identify the filter to impose: Sales[Category] = "Electronics". We want this to override any existing category filter from slicers.
- Step 3 — Wrap the SUM inside CALCULATE and add the filter as a second argument: CALCULATE(SUM(Sales[Amount]), Sales[Category] = "Electronics"). CALCULATE replaces any existing filter on the Category column with this new filter, then evaluates the SUM.
- Step 4 — Name the measure and test it in a card visual. The card should show the same number no matter which category slicer value is selected, confirming the filter is overriding correctly.
Answer: Electronics Revenue = CALCULATE(SUM(Sales[Amount]), Sales[Category] = "Electronics")
2. Write a measure that returns year-to-date revenue, then write a second measure that calculates the year-over-year percentage change compared to the same period last year.
- Step 1 — Confirm the model has a Date table marked correctly in Power BI (Model view → right-click the date table → Mark as date table).
- Step 2 — Write the YTD measure using TOTALYTD. The first argument is the expression; the second is the date column: Revenue YTD = TOTALYTD(SUM(Sales[Amount]), Dates[Date]).
- Step 3 — Write the prior-year comparison measure using SAMEPERIODLASTYEAR inside CALCULATE: Revenue LY = CALCULATE(SUM(Sales[Amount]), SAMEPERIODLASTYEAR(Dates[Date])). This shifts the date filter back one year.
- Step 4 — Write the variance measure. Subtract prior-year from current-year, divide by prior-year. Use DIVIDE to avoid a divide-by-zero error: YoY Variance % = DIVIDE(SUM(Sales[Amount]) - [Revenue LY], [Revenue LY]).
- Step 5 — Format the YoY Variance % measure as a percentage in the Measure tools ribbon. Place all three measures in card visuals and verify that filtering by year or month updates the values logically.
Answer: Revenue YTD = TOTALYTD(SUM(Sales[Amount]), Dates[Date]) | Revenue LY = CALCULATE(SUM(Sales[Amount]), SAMEPERIODLASTYEAR(Dates[Date])) | YoY Variance % = DIVIDE(SUM(Sales[Amount]) - [Revenue LY], [Revenue LY])
3. A student writes the following DAX and gets a blank result on the card visual: Total Orders = COUNTROWS(FILTER(Orders, Orders[Status] = "Complete")). The Orders table has a Status column with the value 'Completed' (with a 'd'). Fix the formula and explain why it returned blank.
- Step 1 — Read the error symptom: the card shows BLANK. COUNTROWS on an empty table returns BLANK in DAX, not zero.
- Step 2 — Trace the cause: FILTER is comparing Status to the string "Complete", but the actual data value is "Completed". The string comparison is exact and case-insensitive by default, but the missing 'd' means no rows match, producing an empty table, so COUNTROWS returns BLANK.
- Step 3 — Fix the string literal to match the actual data value exactly: change "Complete" to "Completed".
- Step 4 — Optionally wrap in CALCULATE for better performance on large tables, using a simple filter argument instead of FILTER: Total Orders = CALCULATE(COUNTROWS(Orders), Orders[Status] = "Completed").
- Step 5 — Test the corrected measure in the card visual; it should now display the count of completed orders.
Answer: Total Orders = CALCULATE(COUNTROWS(Orders), Orders[Status] = "Completed")
4. Decide whether each of the following should be a calculated column or a measure, and justify the choice: (a) Full name = FirstName & " " & LastName; (b) Average order value that changes when a region slicer is applied.
- Step 1 — Evaluate item (a): Full name is a per-row string concatenation. It produces a fixed value for each customer regardless of any report filter. It may be used in a slicer or a table visual as a label. This is a calculated column — store it row by row in the Customers table.
- Step 2 — Write the calculated column: Full Name = Customers[FirstName] & " " & Customers[LastName]. Add this in the Data view, not the Report view.
- Step 3 — Evaluate item (b): Average order value must respond to the region slicer. When the user selects 'West', the average should recalculate using only West orders. A calculated column cannot do this — it is computed at refresh and ignores report filters.
- Step 4 — Write the measure: Avg Order Value = DIVIDE(SUM(Orders[Amount]), COUNTROWS(Orders)). Place it in a card; verify the value changes when different regions are selected in the slicer.
Answer: (a) Calculated column — per-row label, no filter context needed. (b) Measure — must respond dynamically to slicer and filter context.
In class: Scaffolded DAX worksheet: students complete a progression of 12 DAX tasks printed on a worksheet—starting with SUM and COUNTROWS, advancing to CALCULATE with a filter argument, and ending with a year-over-year variance measure—testing each in a card visual. · Error-message decoding: instructor shows four common DAX errors (circular dependency, blank measure, wrong context transition, missing relationship) via screenshots; students match each error to its cause and write the corrected DAX. · KPI measure build: each student creates three business KPIs of their choice from the class dataset (e.g., total revenue YTD, % change vs. prior year, running total) and formats them as card visuals on a blank report page, with units and thousands separator.
Materials: Power BI Desktop, DAX Guide reference (dax.guide), provided .pbix dataset, DAX task worksheet (instructor-prepared), lab log
Check: Instructor runs a provided check query against each student's model; all 12 DAX measures must return values within 0.1% of the answer key. Three KPI measures must render without error and display correct data. PL-300 mapping: Model the data domain; Visualize and analyze the data domain.
Practice check8
Answer each question, then check it. Nothing is recorded; this is your own practice.
Record this lesson on my transcript. Sign in with your key and enrollment email to record lessons (enroll free if you have no key). This page stays open to read and practise.