MICROSOFT POWER BI • DAX AND MEASURES

Prior Period Comparisons — Compute prior period comparisons (SAMEPERIODLASTYEAR, DATEADD) (intro)

Leverage DAX time-intelligence functions to compare current metrics against historical baselines for trend analysis.

Historical Context & Motivation

Comparing current performance against past performance is one of the most fundamental operations in business analytics. Long before modern BI tools existed, accountants and analysts manually compiled prior period comparisons by pulling figures from ledgers, spreadsheets, or databases and placing them side by side — a tedious, error-prone process that nonetheless delivered enormous insight into whether a business was improving, stagnating, or declining. The evolution from manual spreadsheet formulas to declarative, model-aware functions like those in DAX (Data Analysis Expressions) represents a paradigm shift: instead of writing imperative date-arithmetic logic, an analyst declares what time shift to apply and lets the engine resolve the filter context.

1990s
Spreadsheet Era
Excel's VLOOKUP and OFFSET functions became the de facto approach for year-over-year comparisons. Analysts manually offset row references by 12 months, requiring careful maintenance as data grew.
2005
OLAP & MDX Emerge
SQL Server Analysis Services popularized Multidimensional Expressions (MDX), introducing the ParallelPeriod() function — an early model-aware time-intelligence concept that separated the 'what' from the 'how' of date shifting.
2009
DAX Is Born (PowerPivot)
Microsoft released PowerPivot for Excel with the DAX language, including built-in time-intelligence functions such as SAMEPERIODLASTYEAR and DATEADD, bringing OLAP-grade analytics to the tabular model.
2015
Power BI Desktop Launch
Power BI Desktop launched as a standalone BI tool with full DAX support, enabling cloud-published dashboards with prior-period comparisons accessible to non-programmers and data engineers alike.
2020s
Modern DAX Ecosystem
The DAX engine has matured with optimized storage engines, composite models, and DirectQuery enhancements, making time-intelligence calculations performant even on multi-billion-row datasets.

The central question these functions address is deceptively simple: given a measure evaluated under a particular date filter, what would that measure have been for the equivalent period one year — or N intervals — ago? Answering this requires manipulating filter context over a properly structured date table, which is exactly what SAMEPERIODLASTYEAR and DATEADD were designed to do.

Core Principles & Definitions

Before diving into the syntax of individual functions, it is essential to understand the foundational mechanics that all DAX time-intelligence functions share. These functions do not simply subtract days from date columns; they operate by returning modified date tables that replace the current filter context's date range with a shifted equivalent. The engine then re-evaluates the target measure under this new filter context, producing the prior-period value. This architecture mirrors the functional-programming concept of higher-order functions: a time-intelligence function takes a date set as input, transforms it, and returns a new date set that CALCULATE uses to override the original filter.

1

Date Table Requirement

All time-intelligence functions require a contiguous, complete date table — a table with one row per calendar day, marked as the date table in the model. Missing dates produce incorrect or empty results.
2

Filter Context Replacement

Functions like SAMEPERIODLASTYEAR return a table of dates. When wrapped inside CALCULATE, this table replaces the existing date filter, causing the measure to evaluate against the shifted period.
3

SAMEPERIODLASTYEAR

A convenience wrapper equivalent to DATEADD(<dates>, -1, YEAR). It shifts every date in the current filter exactly one year backward — no parameters needed beyond the date column.
4

DATEADD

A generalized time-shift function accepting three arguments: the date column, the number of intervals (positive = future, negative = past), and the interval type (DAY, MONTH, QUARTER, YEAR). Far more flexible than SAMEPERIODLASTYEAR.
5

CALCULATE as the Orchestrator

Neither SAMEPERIODLASTYEAR nor DATEADD produce scalar values on their own. They return date tables that become filter arguments inside CALCULATE, which re-evaluates the base measure under the modified context.
KEY TAKEAWAY
Think of a DAX time-intelligence function as a lens swap on a camera. The scene (your data model) stays the same, but by swapping the lens (the date filter), you see a different period's metrics come into focus. CALCULATE is the camera body that accepts whichever lens you mount — SAMEPERIODLASTYEAR gives you a fixed 'one-year-back' lens, while DATEADD lets you dial in any focal shift you need.

Visual Explanation — Filter Context Shifting

The diagram illustrates how SAMEPERIODLASTYEAR maps the current Q2 2024 filter (cyan dashed outline) to Q2 2023 (violet dashed outline). The base measure evaluates under the original context to yield $420K, while the prior-period measure evaluates the same expression under the shifted context, producing $385K. Both measures use SUM(Sales[Amount]) — only the filter changes.

Notice that both the base measure and the prior-period measure reference the exact same expression — SUM(Sales[Amount]). The only difference is the filter context under which the expression is evaluated. This is the power of DAX's evaluation model: by manipulating the context rather than rewriting the calculation, you achieve clean separation between business logic (what to measure) and temporal logic (when to measure it). This composability is analogous to the decorator pattern in software engineering — you wrap a function with additional behavior without modifying the function itself.

Function Syntax & Evaluation Mechanics

SAMEPERIODLASTYEAR Syntax

SAMEPERIODLASTYEAR
SAMEPERIODLASTYEAR( <dates> )
<dates> — a reference to a date column in a date table marked as Date Table. Returns a single-column table of dates shifted back exactly one year. Semantically equivalent to DATEADD(<dates>, −1, YEAR).

DATEADD Syntax

DATEADD
DATEADD( <dates>, <number_of_intervals>, <interval> )
<dates> — date column reference. <number_of_intervals> — integer; negative shifts to the past, positive to the future. <interval> — one of DAY, MONTH, QUARTER, YEAR. Returns a single-column table of shifted dates.

Measure Pattern: Year-over-Year (YoY) Change

YoY ABSOLUTE CHANGE
YoY Change = [Total Sales] − CALCULATE([Total Sales], SAMEPERIODLASTYEAR('Dates'[Date]))
This measure subtracts last year's sales from the current period's sales, yielding the absolute change. A positive result indicates growth; negative indicates contraction.
YoY PERCENTAGE CHANGE
YoY % = DIVIDE([Total Sales] − [Sales LY], [Sales LY])
Where [Sales LY] is CALCULATE([Total Sales], SAMEPERIODLASTYEAR('Dates'[Date])). The DIVIDE function is preferred over the / operator because it handles division by zero gracefully by returning BLANK() or a specified alternate result.
IMPORTANT: Date Table Configuration
For time-intelligence functions to work correctly, your date table must be marked as a Date Table in Power BI (Table tools → Mark as date table) and must contain a contiguous set of dates with no gaps. A missing date — even a single day — can cause SAMEPERIODLASTYEAR and DATEADD to return unexpected results or BLANK().

DATEADD Intervals — Flexible Time Shifting

While SAMEPERIODLASTYEAR provides a single, hardcoded time shift (−1 year), DATEADD is the generalized form that supports arbitrary shifts across four interval types. Understanding how each interval type maps the current date range to a shifted range is critical for building robust measures. The table below summarizes the most common configurations, and the diagram that follows visualizes how month- and quarter-level shifts behave differently from year-level shifts.

Common DATEADD configurations for prior-period analysis
Use CaseDAX ExpressionShift Description
Prior monthDATEADD('Dates'[Date], −1, MONTH)Shifts every date in the filter back by one calendar month.
Same quarter last yearDATEADD('Dates'[Date], −4, QUARTER)Shifts four quarters back, equivalent to one year at the quarter grain.
Same month last yearDATEADD('Dates'[Date], −12, MONTH)Shifts 12 months back — functionally identical to SAMEPERIODLASTYEAR for monthly granularity.
Prior 7 days (rolling)DATEADD('Dates'[Date], −7, DAY)Shifts each date back by 7 days; useful for week-over-week analysis.
Two years agoDATEADD('Dates'[Date], −2, YEAR)Shifts two full years back; useful for trend lines spanning multiple years.
Starting from an original context of March 2024 (amber), each row shows the result of a different DATEADD shift: −1 MONTH (cyan) yields February 2024 (note the leap-year 29-day month), −1 QUARTER (violet) yields December 2023, and −1 YEAR (pink) yields March 2023 — identical to SAMEPERIODLASTYEAR.
💡 Edge Case: Month-End Alignment
When shifting by MONTH, DAX handles months of different lengths intelligently. Shifting from March 31 by −1 month maps to February 29 (or 28 in non-leap years), effectively clamping to the last valid date. This differs from naïve date arithmetic that might produce an invalid date. Keep this in mind when aggregating daily-grain data across month boundaries.

Worked Example — Year-over-Year Sales Dashboard

Suppose you have a Power BI model with a Sales fact table containing columns [OrderDate] and [Amount], and a dedicated Dates dimension table with a contiguous date range from 2022-01-01 through 2024-12-31, marked as the date table. A slicer on the report selects June 2024. Your goal is to create three measures: current-period sales, prior-year sales, and year-over-year percentage change.

Building a YoY Sales Comparison
1
Step 1 — Define the Base MeasureCreate the base measure that sums the sales amount. This measure respects whatever date filter is active in the current context. Total Sales = SUM(Sales[Amount]) With the slicer set to June 2024, this measure scans the Sales table for rows where OrderDate falls in June 2024.
Total Sales (June 2024) = $142,500
2
Step 2 — Create the Prior-Year Measure Using SAMEPERIODLASTYEARWrap the base measure inside CALCULATE with SAMEPERIODLASTYEAR as the filter modifier: Sales LY = CALCULATE([Total Sales], SAMEPERIODLASTYEAR('Dates'[Date])) The engine takes the current filter (June 2024 dates) and shifts each date back one year, producing the set {2023-06-01 … 2023-06-30}. It then evaluates [Total Sales] under this shifted context.
Sales LY (June 2023) = $128,000
3
Step 3 — Compute YoY Percentage ChangeUse the DIVIDE function to safely compute the percentage change: YoY % Change = DIVIDE([Total Sales] - [Sales LY], [Sales LY]) Substituting values: ($142,500 − $128,000) ÷ $128,000 = $14,500 ÷ $128,000 ≈ 0.1133.
YoY % Change = +11.33%
4
Step 4 — Alternative: Use DATEADD for Prior QuarterTo compare June 2024 with March 2024 (one quarter back) instead, replace the filter argument: Sales PQ = CALCULATE([Total Sales], DATEADD('Dates'[Date], −1, QUARTER)) This shifts June 2024 dates back by one quarter, resolving to March 2024 dates. The result reflects prior-quarter performance, useful for seasonality-adjusted analysis.
Sales PQ (March 2024) = $135,200
5
Step 5 — Format & DeployFormat the YoY % Change measure as a percentage with two decimal places in the Power BI modeling pane. Place all three measures in a card visual or table visual alongside a date slicer. As the user changes the slicer, every measure automatically recalculates because the time-intelligence functions derive their shift from the current filter context — no hardcoded dates required.
Dashboard displays: $142.5K current | $128.0K LY | +11.33% YoY

SAMEPERIODLASTYEAR vs. DATEADD — Strengths & Trade-Offs

Both functions accomplish similar goals, but they occupy different points on the simplicity–flexibility spectrum. Choosing between them depends on the analytical requirements and the maintenance cost you are willing to accept. The table below provides a structured comparison across several dimensions.

Feature comparison of the two time-intelligence functions
DimensionSAMEPERIODLASTYEARDATEADD
ParametersOne (date column only)Three (date column, interval count, interval type)
DirectionBackward only (always −1 year)Forward or backward (positive or negative integer)
Interval TypesYEAR onlyDAY, MONTH, QUARTER, YEAR
ReadabilitySelf-documenting; intent is immediately obviousRequires reading three arguments to understand intent
MaintainabilityLow maintenance; no parameters to misconfigureMedium maintenance; incorrect interval or sign introduces subtle bugs
Use Case FitYoY comparisons exclusivelyMoM, QoQ, WoW, multi-year, and custom-period comparisons
PerformanceIdentical — compiled to the same internal planIdentical when parameters match
WHEN TO CHOOSE WHICH
If your only need is year-over-year analysis, prefer SAMEPERIODLASTYEAR for its clarity and lower cognitive overhead — it is a named, intention-revealing wrapper. If you need month-over-month, quarter-over-quarter, or any other interval, DATEADD is the generalized tool. Think of SAMEPERIODLASTYEAR as a convenience alias, much like how a programming language might offer both array.isEmpty() and array.length === 0 — same semantics, but the named version communicates intent more efficiently.

Connection to Advanced Time Intelligence

The functions introduced in this lesson — SAMEPERIODLASTYEAR and DATEADD — are entry points into a broader family of DAX time-intelligence functions. As your analytical requirements become more sophisticated, you will encounter functions that accumulate values over time, compute running totals, or identify specific date boundaries. The table below previews these advanced counterparts and positions the introductory functions within the larger ecosystem.

Mapping introductory functions to advanced DAX time-intelligence
Introductory FunctionAdvanced CounterpartWhat It Adds
SAMEPERIODLASTYEARPARALLELPERIODShifts by a full period (e.g., entire year) rather than the same date range — useful for comparing full-year totals regardless of current selection.
DATEADDDATESINPERIODReturns a contiguous set of dates from a starting point — enables rolling windows (e.g., trailing 30-day average).
YoY absolute changeTOTALYTD / DATESYTDAccumulates values from the start of the year to the current date — year-to-date running totals.
Simple % changePREVIOUSMONTH / PREVIOUSQUARTERConvenience wrappers for specific period granularities, similar in spirit to SAMEPERIODLASTYEAR but for different intervals.

As you progress, you will also explore custom date tables with fiscal calendars (e.g., 4-4-5 retail calendars), which require the more flexible DATEADD or even manual filter generation with FILTER and ALL. Mastering the introductory functions in this lesson builds the conceptual foundation — filter context replacement, date table dependency, and the CALCULATE pattern — that underpins every advanced time-intelligence technique.

Practice Problems

PROBLEM 1CONCEPTUAL
Explain why DAX time-intelligence functions like SAMEPERIODLASTYEAR require a properly configured, contiguous date table. What would happen if the date table contained gaps (e.g., missing weekends)?
PROBLEM 2BASIC CALCULATION
Given a measure Total Revenue = SUM(Orders[Revenue]), write a DAX measure called Revenue LY that returns the same metric for the corresponding period one year ago. Then write a second measure Revenue MoM that returns the metric for the prior month.
PROBLEM 3INTERMEDIATE
A report slicer selects Q3 2024 (July through September). You have a measure [Total Profit] that returns $580,000 for Q3 2024 and $510,000 for Q3 2023. Write a single DAX measure Profit YoY % that computes the year-over-year percentage change, and calculate its expected value. Explain how DIVIDE handles the case when Q3 2023 profit is zero.
PROBLEM 4APPLIED
A retail analytics team wants a report card that shows: (a) current month sales, (b) same month last year sales, (c) YoY absolute change, and (d) trailing-quarter sales (sum of the prior 3 months, not including the current month). Write all four DAX measures assuming a base measure [Sales Amount] and a date table 'Calendar'[Date]. For measure (d), decide whether DATEADD or a different approach is needed, and justify your choice.
PROBLEM 5CRITICAL THINKING
Consider a company that operates on a fiscal calendar starting April 1 (i.e., fiscal year 2024 = April 2024 through March 2025). A developer uses SAMEPERIODLASTYEAR to compute YoY comparisons. Will the function produce correct fiscal-year-over-fiscal-year results, or will it compare calendar years? Analyze the underlying mechanism and propose a solution if the function falls short.

Lesson Summary

This lesson introduced two foundational DAX time-intelligence functions for prior-period analysis. SAMEPERIODLASTYEAR is a single-parameter convenience function that shifts the current date filter exactly one year into the past, while DATEADD generalizes the concept by accepting an interval count and type (DAY, MONTH, QUARTER, YEAR), enabling month-over-month, quarter-over-quarter, and custom-period comparisons. Both functions return a table of shifted dates and must be used as filter arguments inside CALCULATE, which replaces the current filter context with the shifted dates and re-evaluates the target measure.

Key prerequisites include a contiguous date table marked in the model and the use of DIVIDE for safe percentage calculations. The standard YoY pattern — DIVIDE([Measure] − [Measure LY], [Measure LY]) — is a reusable template that extends to any interval. Looking ahead, these introductory functions form the foundation for advanced techniques such as PARALLELPERIOD, DATESINPERIOD, and year-to-date accumulators like TOTALYTD.

Varsity Tutors • Microsoft Power BI • Prior Period Comparisons — Compute prior period comparisons (SAMEPERIODLASTYEAR, DATEADD) (intro)