Understanding Common DAX Errors: A Simple Guide
Photo by Patrick Hendry on Unsplash
Table of contents
- Scenario 01
- How Does KEEPFILTERS Affect the Calculation?
- Practical Example:
- Key Takeaway:
- Scenario 02
- Goal:
- Why This Causes an Error:
- The Core Problem:
- Example to Clarify:
- How to Solve This:
- Key Takeaways:
- Scenario 03
- Let’s Break Down the Problem
- What Is Context Transition?
- Detailed Example to Illustrate the Problem:
- Finding Context Transition Issues:
- How to Resolve This Issue?
- Conclusion:
- Scenario 04
- Clarifying the Requirement:
- Example to Clarify:
- Summary of the Issue:
- Understanding Filter Contexts in DAX
- Why Does This Matter?
- Why Does DAX Care About This Distinction?
- Why Is This Relevant to the Error?
- Summary:
- Sample file(live) from SQLBI to play with;
Scenario 01
What is the purpose of KEEPFILTERS in the following?
Let’s explore the concept KEEPFILTERS
with an example to clarify how it works.
Suppose you have a sales table with the following data:
Product | Quantity | Net Price | Country | Brand |
A | 10 | 50 | Canada | Contoso |
B | 5 | 300 | United States | Contoso |
C | 20 | 25 | Canada | Contoso |
D | 15 | 70 | United States | Fabrikam |
E | 3 | 500 | Canada | Contoso |
You want to calculate the total sales amount, but only for transactions where:
The quantity multiplied by the net price is greater than 1000.
The country is either "Canada" or "United States."
The brand is "Contoso."
The DAX expression provided is:
CALCULATE (
[Sales Amount],
KEEPFILTERS ( Sales[Quantity] * Sales[Net Price] > 1000 ),
Customer[Country] IN { "Canada", "United States" },
'Product'[Brand] = "Contoso"
)
How Does KEEPFILTERS
Affect the Calculation?
Without KEEPFILTERS
:
Let’s say there’s already a filter applied to the data model that limits the sales to products with a net price greater than 100.
When the expression runs:
The condition
(Sales[Quantity] * Sales[Net Price] > 1000)
would replace any existing filters on theSales
table.The original filter on
Net Price > 100
would be overridden by this new filter, and the only condition applied would beSales[Quantity] * Sales[Net Price] > 1000
.
This could lead to unexpected results because the initial filter context would be lost.
With KEEPFILTERS
:
By using KEEPFILTERS
, the original filter on Net Price > 100
remains active while also applying the additional filter (Sales[Quantity] * Sales[Net Price] > 1000)
.
This layered approach results in a more restrictive filter. Only rows that meet both the original filter and the new condition will be included.
Practical Example:
Using the data above, let’s break it down:
Base Filter (assuming it exists in the context):
Net Price > 100
Affects products B, D, and E.
Without
KEEPFILTERS
:The new filter
(Sales[Quantity] * Sales[Net Price] > 1000)
is applied.This overrides the base filter and selects only product E (where
3 * 500 = 1500
).The existing filter on
Net Price > 100
is ignored.
With
KEEPFILTERS
:The existing filter
Net Price > 100
remains.The additional filter
(Sales[Quantity] * Sales[Net Price] > 1000)
is applied.Both filters together result in selecting only product E, because it meets both conditions.
In this case, the results are similar, but in more complex scenarios with multiple layers of filters, using KEEPFILTERS
ensures that you’re combining, not replacing, filter conditions.
Key Takeaway:
Without
KEEPFILTERS
: The new filter can override existing filters on the same columns.With
KEEPFILTERS
: The new filter is layered on top of existing filters, ensuring all conditions are respected.
This approach gives you more precise control over the filter context in complex DAX calculations.
Scenario 02
"The expression contains columns from multiple tables, but only columns from a single table can be used in a True/False expression that is used as a table filter expression."
The error happens because DAX does not allow you to directly compare columns from two different tables inside a CALCULATE
function when using logical conditions like =
or >
. The error message specifically states: “The expression contains columns from multiple tables, but only columns from a single table can be used in a True/False expression that is used as a table filter expression.”
Let’s break down why this happens using an example.
Suppose you have two tables:
Customer Table:
CustomerID | CountryRegion |
1 | United States |
2 | Canada |
3 | United States |
Store Table:
StoreID | CountryRegion |
1 | United States |
2 | Canada |
3 | Mexico |
Goal:
Let's assume we want to calculate the total sales amount for customers who are in the same country as the store they are purchasing from.
DAX expression is:
Same Country Sales :=
CALCULATE (
[Sales Amount],
KEEPFILTERS ( Customer[CountryRegion] = Store[CountryRegion] )
)
❌
Why This Causes an Error:
The issue arises because DAX does not allow a direct comparison of columns from different tables in the filter condition when using CALCULATE
. This is because DAX expects a logical expression within KEEPFILTERS
to work on a single table.
In case, Customer[CountryRegion]
and Store[CountryRegion]
are from different tables (Customer and Store). DAX cannot evaluate this condition across tables directly.
The Core Problem:
DAX requires that the filter condition involves a single table. The comparison of columns from multiple tables within the same logical expression is not allowed in this context.
Example to Clarify:
Imagine your original code without the KEEPFILTERS
function:
daxCopy code=
CALCULATE (
[Sales Amount],
Customer[CountryRegion] = Store[CountryRegion]
)
❌
This code will also produce the same error because the condition compares columns across tables.
How to Solve This:
To fix this issue, you need to first relate the tables using a relationship or a virtual table that combines the data from both tables.
Using a Virtual Table:
You can create a virtual table that joins the Customer and Store tables and then apply your filter:
Same Country Sales :=
CALCULATE (
[Sales Amount],
FILTER(
CROSSJOIN(Customer, Store),
Customer[CountryRegion] = Store[CountryRegion]
)
) ✅
Here, CROSSJOIN
creates a virtual table containing all possible combinations of rows from both tables, and then FILTER
applies the condition to find matching countries.
Key Takeaways:
DAX requires a single table for logical comparisons in filter expressions within
CALCULATE
.When comparing columns from different tables, use
RELATED
,LOOKUPVALUE
, or create virtual tables to bring both columns into the same context.The error occurs because DAX does not support directly comparing columns from different tables in a simple filter expression.
Scenario 03
"A function ‘CALCULATE‘ has been used in a True/False expression that is used as a table filter expression. This is not allowed"
The error occurs because of the use
CALCULATE
of a logical expression within anotherCALCULATE
. Specifically, the issue is that when the filter argument is a logical expression (e.g.,Sales[Net Price] > ...
), DAX does not allow the use of any function that triggers context transition inside that expression.Let’s Break Down the Problem
Here’s the code causing the error:
Sales Top Prices := CALCULATE ( [Sales Amount], Sales[Net Price] > CALCULATE ( MAX ( Sales[Net Price] ) * 0.9, REMOVEFILTERS() ) ) ❌
The goal of this measure is to return the total sales amount for products where the net price is in the top 10% of all prices.
Why Does This Code Fail?
The issue is that you’re using a
CALCULATE
function inside a logical expression (Sales[Net Price] > ...
) within the mainCALCULATE
. This is not allowed becauseCALCULATE
performs context transition, which cannot be nested in this way when used as a filter predicate.What Is Context Transition?
Context transition occurs when a row context (which usually applies within iterators like
SUMX
,FILTER
, etc.) is converted into a filter context. This happens when you useCALCULATE
, which is designed to transition from a row context to a filter context.However, in this case, the issue is that
CALCULATE
is used inside a logical expression that itself is within anotherCALCULATE
. DAX cannot handle the context transition within this kind of nested logical filter expression.Detailed Example to Illustrate the Problem:
Data Example:
Suppose you have a
Sales
table with the following data:|
Product
|Net Price
|Quantity
|Sales Amount
| | --- | --- | --- | --- | |A
|100
|10
|1000
| |B
|80
|5
|400
| |C
|120
|8
|960
| |D
|60
|15
|900
|You want to calculate the total sales amount for products where the net price is in the top 10% of all prices.
You try this:
Sales Top Prices := CALCULATE ( [Sales Amount], Sales[Net Price] > CALCULATE ( MAX ( Sales[Net Price] ) * 0.9, REMOVEFILTERS() ) )
The inner
CALCULATE
is used to determine the threshold for the top 10% of prices:CALCULATE ( MAX ( Sales[Net Price] ) * 0.9, REMOVEFILTERS() )
This returns 90% of the highest price (which is 120, so the result is 108).
The outer
CALCULATE
is supposed to filter rows where theSales[Net Price]
is greater than 108:Sales[Net Price] > 108
But because the logical expression involves a
CALCULATE
(which causes context transition), DAX cannot handle this scenario.
Finding Context Transition Issues:
A general rule to identify potential context transition issues is:
- If you see a
CALCULATE
inside a logical expression (like>
,<
,=
, etc.) in another, it likely causes this error.
How to Resolve This Issue?
To avoid the problem, you need to break down the calculation into steps and avoid nesting context transitions in logical expressions. Here’s a possible solution:
Step 1: Calculate the Threshold Separately
Top Price Threshold :=
CALCULATE ( MAX ( Sales[Net Price] ) * 0.9, REMOVEFILTERS() )
Step 2: Use This Threshold in Your Measure
Sales Top Prices :=
CALCULATE (
[Sales Amount],
Sales[Net Price] > [Top Price Threshold]
)
This approach separates the calculation of the threshold from the filtering logic, avoiding the issue with nested context transitions.
Conclusion:
The error happens because you’re trying to nest CALCULATE
, which performs context transition, inside a logical filter expression within another CALCULATE
. The key to avoiding this issue is to split your calculation into multiple steps, ensuring that context transitions are not nested in logical expressions.
Context transition is always triggered by
CALCULATE
, but it’s especially problematic when nested inside logical expressions are used as filter conditions.The error happens because DAX cannot handle a context transition inside a logical expression used for filtering.
The best practice is to break down the logic into smaller, separate steps to avoid this issue.
Scenario 04
"A function ‘PLACEHOLDER’ has been used in a True/False expression that is used as a table filter expression. This is not allowed"
Why Storing the Measure in a Variable Doesn’t Always Work:
In some cases, simply storing the measure in a variable (like in the first example) might not solve the issue. This is because the real problem could be that the logical condition itself is too ambiguous. For example:
- Should the margin filter be applied at the customer level, the store level, or across all sales transactions?
Clarifying the Requirement:
The key issue here is understanding the target of the filter:
Customer Level: Should the filter apply only if a customer’s overall margin is greater than 60%?
Store Level: Should the filter apply only if a store’s overall margin is greater than 60%?
Transaction Level: Should the filter apply to each sales transaction where the margin is greater than 60%?
Example to Clarify:
Suppose you want to filter sales where the margin at the transaction level is greater than 60%. You could rewrite the measure as:
Sales Profitable :=
CALCULATE (
[Sales Amount],
Sales[Profit] / Sales[Sales Amount] > 0.6
)
Here, the logical condition directly references columns from the Sales
table, making it clear that the filter applies to individual transactions. This approach avoids the context transition issue because it doesn’t involve any ambiguous measure reference.
Summary of the Issue:
Measure References in Logical Expressions: The core issue is using measures (which involve context transition) in logical expressions inside
CALCULATE
. DAX cannot handle context transition in these scenarios.PLACEHOLDER Error: The error refers to a "PLACEHOLDER" because DAX replaces the measure reference with a
CALCULATE
function internally, causing the context transition.Fixing the Issue: Either evaluate the measure first using a variable or clarify the filtering logic to avoid using ambiguous measure references in logical expressions.
Clarifying Requirements: Ensure that the filter target is clear (e.g., customer level, store level, transaction level) to write an effective DAX measure.
By understanding the context transition mechanism and the limitations of logical expressions within CALCULATE
, you can avoid these common pitfalls in DAX.
Understanding Filter Contexts in DAX
When we talk about "margin," it’s essential to understand where and how this margin is being evaluated:
Transaction Level: The margin could be calculated for each sales transaction (e.g., one row in the
Sales
table).Customer Level: The margin could be aggregated for a customer across all their purchases.
Store Level: The margin could be aggregated for an entire store across all its sales.
In DAX, when you use a measure like, the value of that measure depends on the current filter context. The ambiguity lies in what context you want [Margin %] > 0.6
to apply to.
Why Does This Matter?
Let’s break down what happens in different contexts.
Transaction-Level Margin: If you want to filter sales where the margin is greater than 60% for each transaction, you would write:
CALCULATE( [Sales Amount], Sales[Profit] / Sales[Sales Amount] > 0.6 )
Here, you’re directly referencing the columns
Sales[Profit]
andSales[Sales Amount]
within the filter condition. This applies the filter to each row in theSales
table.Customer-Level Margin: If you want to filter sales based on a customer's overall margin (e.g., across all transactions for that customer), you would need to aggregate the margin for each customer and then apply the filter:
CALCULATE( [Sales Amount], CALCULATE( [Margin %], ALLEXCEPT(Customer, Customer[CustomerID]) ) > 0.6 )
Here, the margin is calculated at the customer level, and the filter context is adjusted accordingly.
Store-Level Margin: Similarly, if the goal is to filter based on a store’s overall margin, you would adjust the filter context to the store level:
CALCULATE( [Sales Amount], CALCULATE( [Margin %], ALLEXCEPT(Store, Store[StoreID]) ) > 0.6 )
Why Does DAX Care About This Distinction?
DAX operates based on filter contexts, and the same measure (e.g., [Margin %]
) can produce different results depending on the level of granularity. When you say [Margin %] > 0.6
, DAX needs to know whether you mean:
Margin calculated at the transaction level?
Margin aggregated at the customer level?
Margin aggregated at the store level?
This context determines how the filter is applied and what rows get included in the calculation.
Why Is This Relevant to the Error?
In the original measure:
Sales Profitable :=
CALCULATE(
[Sales Amount],
[Margin %] > 0.6
)
DAX does not know how to apply [Margin %] > 0.6
because [Margin %]
is a measure, and it might involve context transition (depending on how it’s defined). If [Margin %]
is calculated at a different level (e.g., customer or store), the filter becomes ambiguous.
That’s why the error suggests that the filter definition is incomplete—you need to be explicit about what level the filter should be applied at.
Tip: When the measure defined([Margin]) is based on another measure this pattern could be identified.
Summary:
The explanation about customer-level, store-level, and transaction-level margins is not about redefining "what margin is" but about clarifying how DAX calculates and applies filters in different contexts.
The ambiguity comes from DAX needing to know at what level
[Margin %] > 0.6
should be applied, which can vary depending on the scenario.In other calculations like, this ambiguity is usually less problematic because "sales amount" typically applies consistently across contexts.
In essence, DAX is very context-sensitive, and clarifying the context helps ensure that the measure works as intended, avoiding errors and unexpected results.