If you have spent more than a week writing DAX, you have hit it: a calculated column that needs the “current” value of a row and a value from the same table one level up. That is exactly what EARLIER is for — and exactly why most beginners find it confusing. This guide explains what EARLIER actually references, shows a worked example, and then shows the patterns that make you not need it at all.
What EARLIER really does
EARLIER returns the value of a column in an outer row context — a row context created by a parent iterator, not the innermost one you are currently in.
A row context is created by:
- a calculated column (one per row of the table), and
- iterator functions like
SUMX,FILTER,RANKX,ADDCOLUMNS,MAXX.
When an iterator runs inside a calculated column, you get two row contexts stacked: the column’s own row, and the row the iterator is looping over. EARLIER reaches the outer one. EARLIEST reaches two levels up.
The classic example: rank within a category
Say you have a Sales table and want a column that ranks each row by Amount within its own Product. A naive RANKX in a column ranks across the whole table, not per product. EARLIER fixes that:
Rank within Product =
RANKX (
FILTER (
Sales,
Sales[Product] = EARLIER ( Sales[Product] )
),
Sales[Amount],
,
DESC,
DENSE
)
Here is what happens step by step:
- The calculated column creates a row context for each
Salesrow. FILTERiteratesSalesagain, creating a second row context.- Inside
FILTER,EARLIER ( Sales[Product] )pulls the product of the outer (column) row, so the filter keeps only rows of the same product. RANKXthen ranks only within that subset.
Without EARLIER, the inner Sales[Product] would refer to the row FILTER is currently scanning — which is always equal to itself, so the filter would never narrow correctly.
When you should NOT reach for EARLIER
EARLIER only works in a calculated column, because it depends on a row context. Most real reporting is better done with measures, which have no row context and therefore cannot use EARLIER at all. Three modern replacements:
1. Use a measure with RANKX and a visual context
If the ranking is for a visual, do it as a measure:
Rank by Product Measure =
RANKX (
ALLSELECTED ( Sales[Product] ),
[Total Amount],
,
DESC,
DENSE
)
This ranks products in the current visual filter, no column, no EARLIER.
2. Use variables to capture the outer value
When you do need a column, a variable is clearer than EARLIER and easier to read:
Rank within Product (var) =
VAR CurrentProduct = Sales[Product]
RETURN
RANKX (
FILTER ( Sales, Sales[Product] = CurrentProduct ),
Sales[Amount],
, DESC, DENSE
)
CurrentProduct captures the outer row context before the inner FILTER shadows it. Same result, no EARLIER keyword, far easier to debug.
3. Use window functions (Power BI / DirectQuery friendly)
Newer Power BI supports RANK and INDEX window functions that handle grouping natively:
Rank within Product (window) =
RANK ( DENSE, Sales, ORDERBY ( Sales[Amount], DESC ), PARTITIONBY ( Sales[Product] ) )
This reads top to bottom and avoids both EARLIER and manual FILTER subsets.
Performance note
EARLIER wrapped in FILTER ( FullTable, ... ) forces a row-by-row scan of the whole table for every row — it is one of the slower patterns in DAX. On a large table, prefer the window function or a pre-grouped summary table. See power-bi-dax-optimization and power-bi-performance-analyzer-guide for measuring it.
FAQ
Q: Is EARLIER deprecated? A: No, it still works. But variables and window functions are preferred because they are easier to read and usually faster.
Q: Why do I get an error “EARLIER has no outer row context”?
A: You used it where no parent row context exists — typically inside a measure, which has a filter context but no row context. Capture the value with a variable inside SUMX instead, or rewrite as a measure.
Q: EARLIER vs EARLIEST?
A: EARLIER goes one level up; EARLIEST goes to the top level regardless of depth. Use EARLIEST only when you are three or more iterators deep.