Introduction
In DAX, lineage is an important concept, and it is vital to understand how to work with and manipulate it.
As I did in past articles, I will use DAX queries to explain this concept and its effects.
I start with a simple query to get the order count for the product of the brand “Adventure Works”:
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
This is an extract of the result from the query:
This query returns 180 rows. Keep it in mind, as it will be important later on.
Next, I will introduce a filter for a specific month and show the lineage’s role.
Set the lineage
I will add a filter for April 2026:
DEFINE
VAR YearMonthFilter = 202604
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,'Date'[MonthKey] = YearMonthFilter
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
In this case, I define a variable and set the value to 202604.
Next, I add it as a filter to the CALCULATETABLE() function.
Nothing special so far.
This is the result:

In this case, the lineage is not important, as a scalar value sets the filter.
But we can set a lineage by using the TREATAS() function:
DEFINE
VAR YearMonthFilter = TREATAS({ 202604 }, 'Date'[MonthKey])
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
As you can see, the introduction of TREATAS() allows us to pass the variable as a filter. CALCULATETABLE() uses the lineage set by TREATAS() as a filter on the column ‘Date'[MonthKey].
The result doesn’t change, but the query is simpler, as I don’t need to pass the condition as “column equals the filter-value”.

In fact, Power BI uses this form all the time when it passes filters set in a report to the semantic model.
But it does differently:
It defines variables, sets the lineage and adds all filters directly to SUMMARIZECOLUMNS():
DEFINE
VAR YearMonthFilter = TREATAS({ 202604 }, 'Date'[MonthKey])
VAR SelectedBrand = TREATAS( { "Adventure Works" }, 'Product'[BrandName])
EVALUATE
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,YearMonthFilter
,SelectedBrand
,"Order Count", [Online Order Count]
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Clearing the lineage
You might encounter situations where you need to clear the lineage.
The method for doing it varies depending on whether you have a single or multiple values as a filter.
For example, look at the following code, where I use VALUE() to remove the lineage on the previous expression:
DEFINE
VAR YearMonthFilter = TREATAS({ 202604 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = VALUE(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter_cleared
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
This is the error delivered by Power BI:

The engine cannot work with the filter in line 71 because it no longer has lineage.
It will work in this form:
DEFINE
VAR YearMonthFilter = TREATAS({ 202604 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = VALUE(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,'Date'[MonthKey] = YearMonthFilter_cleared
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
As you can see here, the query returns the same result as before:

Notice the change of the filter argument in line 91.
But there is a simpler way of clearing the lineage when working with measures.
Look at the following query with the Measure [Order Count full year], which calculates the order count for the entire year:
DEFINE
MEASURE 'All Measures'[Order Count full year] =
VAR SelYear = TREATAS({ SELECTEDVALUE('Date'[Year]) }, 'Date'[Year])
RETURN
CALCULATE([Online Order Count]
,REMOVEFILTERS('Date')
,SelYear
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
,"Order Count full year", [Order Count full year]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
This is an extract of the result:

Now I add a scalar value to the variable:
DEFINE
MEASURE 'All Measures'[Order Count full year] =
VAR SelYear = TREATAS({ SELECTEDVALUE('Date'[Year]) }, 'Date'[Year])
VAR SelYear_Plus1 = SelYear + 0
RETURN
CALCULATE([Online Order Count]
,REMOVEFILTERS('Date')
,SelYear_Plus1
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
,"Order Count full year", [Order Count full year]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
This operation clears the lineage, and the measure no longer works:

What I can still do is use an equal filter to get the previous result:
DEFINE
MEASURE 'All Measures'[Order Count full year] =
VAR SelYear = TREATAS({ SELECTEDVALUE('Date'[Year]) }, 'Date'[Year])
VAR SelYear_Plus1 = SelYear + 0
RETURN
CALCULATE([Online Order Count]
,REMOVEFILTERS('Date')
,'Date'[Year] = SelYear_Plus1
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
,"Order Count full year", [Order Count full year]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Now I get the same result as before:

And now, let’s use multiple values as a filter.
For example, two months:
DEFINE
VAR YearMonthFilter = TREATAS({ 202604, 202605 }, 'Date'[MonthKey])
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Here is the result of this query:

One way to remove the lineage of a variable with multiple values is to use SUMMARIZECOLUMNS():
DEFINE
VAR YearMonthFilter = TREATAS({ 202604, 202605 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = SUMMARIZECOLUMNS(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter_cleared
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Unfortunately, this method removes the filter altogether, and all months are returned in 180 rows (The same number of rows as with the initial query):

Technically, the lineage is not cleared because the query still works, but the month filter is removed.
But when you try using the variable “YearMonthFilter_cleared” with an IN operator, it doesn’t work anymore:

In this context, I tried other functions, like DISTINCT() and VALUES(). While DISTINCT() had no effect, VALUES() has.
For example, while this query doesn’t work:
DEFINE
VAR YearMonthFilter = TREATAS({ 202604, 202605 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = VALUES(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,YearMonthFilter_cleared
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Here is the error message:

This works when using an IN operator, which indicates that the lineage is cleared when using VALUES():
DEFINE
VAR YearMonthFilter = TREATAS({ 202604, 202605 }, 'Date'[MonthKey])
VAR YearMonthFilter_cleared = VALUES(YearMonthFilter)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[MonthShortName]
,'Date'[MonthKey]
,'Product'[ProductCategoryName]
,"Order Count", [Online Order Count]
)
,'Product'[BrandName] = "Adventure Works"
,'Date'[MonthKey] IN YearMonthFilter
)
ORDER BY 'Date'[MonthKey]
,'Product'[ProductCategoryName]
Here is the result of the query:

The documentation for VALUES() states that this function requires a table or column reference.
But this behaviour shows that it depends on how the variable is used in the query.
As the variable has a lineage set, VALUES() accept it as a column reference.
Manipulate the lineage
Next, let’s change how we apply a filter by manipulating the lineage.
I want to create a report showing all online orders by country, along with the orders served by stores in each country.
For example, I have 68 customer orders from Germany in April 2026. I want to see how many orders have been served by stores in that country, if any.
Something like this:

I can do it by working with a variable:
DEFINE
MEASURE 'All Measures'[Orders served from Country] =
VAR SelCountry = SELECTEDVALUE('Customer'[RegionCountryName])
RETURN
CALCULATE([Online Order Count]
,REMOVEFILTERS(Customer[RegionCountryName])
,'Store'[RegionCountryName] = SelCountry
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[Month]
,'Date'[MonthKey]
,'Customer'[RegionCountryName]
,"Order Count", [Online Order Count]
,"Order Count Country Check", [Order Count Country Check]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[Year]
,'Date'[Month]
,'Customer'[RegionCountryName]
In this approach, I store the current country in a variable. Then I remove the filter from Customer[RegionCountryName]. And I replace it with a filter on ‘Store'[RegionCountryName].
This is the result:

Or I can do it in this way by using TREATAS():
DEFINE
MEASURE 'All Measures'[Orders served from Country] =
CALCULATE([Online Order Count]
,REMOVEFILTERS(Customer[RegionCountryName])
,TREATAS(VALUES('Customer'[RegionCountryName])
,'Store'[RegionCountryName])
)
EVALUATE
CALCULATETABLE(
SUMMARIZECOLUMNS('Date'[Year]
,'Date'[Month]
,'Date'[MonthKey]
,'Customer'[RegionCountryName]
,"Order Count", [Online Order Count]
,"Orders served from Country", [Orders served from Country]
)
,'Product'[BrandName] = "Adventure Works"
)
ORDER BY 'Date'[Year]
,'Date'[Month]
,'Customer'[RegionCountryName]
In this approach, I again remove the filter from Customer[RegionCountryName]. But then I use TREATAS() to switch the filter lineage for the current country to ‘Store'[RegionCountryName].
In this approach, I don’t need a variable; I can directly filter the store table by the current country.
This code is much shorter but can be harder to understand for readers who don’t know how TREATAS() works.
Working with tables
Since we can create ad hoc tables in DAX, we might run into issues because those tables lack lineage.
I can start writing code about this, but SQLBI already did this, and you can read their article, which is very well explained:
You can find it here:
https://www.sqlbi.com/articles/understanding-data-lineage-in-dax
Conclusion
The concept of lineage can be hard to understand, even though we use it all the time when working with filters in DAX.
Power BI generates code using TREATAS() all the time when it applies report filters.
And sometimes it can lead to simpler DAX code when you know how to manipulate it efficiently.
This can become vital when I create a table with DAX from existing tables. The table will retain the lineage. This can lead to issues when I try to add relationships to the source tables. Without clearing the lineage, I will encounter an error due to a circular dependency.
I encourage you to start experimenting with the concepts shown here and to try to optimise your DAX code.
Although “optimise” is the wrong word, as I didn’t notice any performance improvements when using the variants shown.
But having DAX code which is shorter and easier to read can be an optimisation by itself.
Have fun working with it.
References
A SQLBI article about working with lineage:
https://www.sqlbi.com/articles/understanding-data-lineage-in-dax
Like in my previous articles, I use the Contoso sample dataset. You can download the ContosoRetailDW Dataset for free from Microsoft here.
The Contoso Data can be used freely under the MIT License, as described in this document. I changed the dataset to shift the data to contemporary dates.