The Analyst's Path

Year-over-year in DAX

Year-over-year in DAX compares a measure with the same period one year earlier, such as March 2024 against March 2023. To compare against history you need a function that shifts the current date filter. SAMEPERIODLASTYEAR does that for the year case, and DATEADD handles any other shift, such as a month or a quarter. The percentage is the change divided by last year's sales, written with DIVIDE so that a period with no prior year returns blank instead of an error.

4 min readPart of DAX II & Time IntelligenceOpen in course mode

Advertisement

To compare against history you need a function that shifts the current date filter. The friendliest is SAMEPERIODLASTYEAR, which takes the dates in context and returns the identical span one year earlier:

Sales LY = CALCULATE( [Total Sales], SAMEPERIODLASTYEAR( 'Date'[Date] ) )

In a "March 2024" cell this returns March 2023's sales. In a "Q3 2024" cell it returns Q3 2023, and in a full-year cell it returns the whole prior year. It always shifts by one year, and it always returns a period of the same shape and length as the one in context. That's its entire job, and it's also its limitation.

When you need any other shift (a month, a quarter, three years), reach for the general tool, DATEADD(dates, number_of_intervals, interval):

Sales LY (DATEADD) = CALCULATE( [Total Sales], DATEADD( 'Date'[Date], -1, YEAR ) )
Sales Prev Month    = CALCULATE( [Total Sales], DATEADD( 'Date'[Date], -1, MONTH ) )
Sales Prev Quarter  = CALCULATE( [Total Sales], DATEADD( 'Date'[Date], -1, QUARTER ) )

When the context is a clean calendar period, DATEADD(…, -1, YEAR) gives the identical result to SAMEPERIODLASTYEAR, so use whichever reads better. Most analysts keep SAMEPERIODLASTYEAR for the year case because the name documents intent. DATEADD comes into its own with the month and quarter shifts, which SAMEPERIODLASTYEAR simply can't express. Those shifts are what power month-over-month and quarter-over-quarter comparisons.

Advertisement

How is PARALLELPERIOD different?

Then there's the subtle third cousin, PARALLELPERIOD. PARALLELPERIOD(dates, number, interval) also shifts, but it always returns the full interval and ignores the granularity in context. At a "March 2024" cell, PARALLELPERIOD('Date'[Date], -1, YEAR) returns all of 2023. You don't get March 2023 on its own, because the function jumps to the parallel year and hands back the whole thing. That makes it the tool for "this month as a share of the whole prior year," and a trap if you expected same-month behaviour. So keep one picture in your head. SAMEPERIODLASTYEAR and DATEADD preserve the shape of the period, while PARALLELPERIOD snaps to the whole interval.

All three need the marked Date table. All three return a table of dates that CALCULATE then imposes as a filter, and none of them is a value on its own.

YoY and YoY% measures

With a "this period" measure and a "last year" measure in hand, the two figures every dashboard wants fall out straight away. First, the absolute year-over-year change:

Sales YoY = [Total Sales] - CALCULATE( [Total Sales], SAMEPERIODLASTYEAR( 'Date'[Date] ) )

Then the percentage, which is what executives actually read. Write it with DIVIDE, never the / operator. That way a period with no prior-year data, like the first year in the model, returns blank instead of #DIV/0! or a nonsensical spike:

Sales YoY % =
DIVIDE(
    [Total Sales] - CALCULATE( [Total Sales], SAMEPERIODLASTYEAR( 'Date'[Date] ) ),
    CALCULATE( [Total Sales], SAMEPERIODLASTYEAR( 'Date'[Date] ) )
)

Format that measure as a percentage in the measure dialog and it reports growth for every product × region × month combination at once. It's the same measure Worked example 1 uses. There we run it over a two-year Indian monthly series and verify a month by hand.

Here's a professional refinement you'll meet again when we get to VAR. Notice that CALCULATE([Total Sales], SAMEPERIODLASTYEAR(…)) is written twice. It's computed twice as well. That wastes work, and it's error-prone if you ever edit one copy and not the other. The VAR keyword fixes both problems, and we rebuild this measure that way in VAR for readable measures.

Advertisement

Part of the course

This page is part of DAX II & Time Intelligence (EX5.02), in Phase 5: Industry and sector mastery. The module's flashcards and its quiz are in the app.