Following on from my last post on the Query Memory Limit in Power BI, in this post I want to look at one of the most common DAX antipatterns and its effect on query memory usage: filtering on a whole table, rather than an individual column, in the filter parameters of the Calculate() function. A lot has already been written on this particular antipattern from the point of view of query performance – for example see here and here – but it’s only with the recent addition of the Execution Metrics Profiler/Log Analytics event that you can see how bad it is for memory usage too.
Here’s a simple example. Using a semantic model based on the UK Land Registry’s Price Paid data, with two dimension tables (Date and Property Type) and a fact table called Property Transactions with just over a million rows:

…let’s say that you write a measure that counts the number of property transactions for detached properties, ie counting the rows in the Property Transactions table where the Property Type column contains the value “D”. Here’s a first attempt at that:
Detached Count V1 =
CALCULATE (
COUNTROWS ( 'Property Transactions' ),
FILTER ( 'Property Transactions', 'Property Transactions'[Property Type] = "D" )
)
Notice that this measure follows the antipattern of filtering on the whole Property Transactions table with the Filter() function just to filter on the Property Type column.
Here’s a DAX query that shows the results of this measure for all Postcodes. It returns around 153000 rows to show the memory impact of the way the measure is written.
EVALUATE
SUMMARIZECOLUMNS(
'Property Transactions'[Postcode],
"Detached Count", [Detached Count V1]
)
Here’s what the Execution Metrics event returns for this query:
{
"timeStart": "2024-06-28T15:41:59.951Z",
"timeEnd": "2024-06-28T15:42:00.388Z",
"durationMs": 438,
"vertipaqJobCpuTimeMs": 47,
"queryProcessingCpuTimeMs": 391,
"totalCpuTimeMs": 438,
"executionDelayMs": 0,
"approximatePeakMemConsumptionKB": 22246,
"commandType": 27,
"queryDialect": 3,
"queryResultRows": 152583
}
In this case the memory usage of the query, as given by the approximatePeakMemConsumptionKB metrci, is 22246KB or about 23MB.
Now consider this version of the measure:
Detached Count V2 =
CALCULATE (
COUNTROWS ( 'Property Transactions' ),
'Property Transactions'[Property Type] = "D"
)
Running the same DAX query as before but with this new measure you get the same results but with a much lower memory usage of 8734KB or 9MB:
{
"timeStart": "2024-06-28T15:56:09.431Z",
"timeEnd": "2024-06-28T15:56:09.602Z",
"durationMs": 172,
"vertipaqJobCpuTimeMs": 31,
"queryProcessingCpuTimeMs": 125,
"totalCpuTimeMs": 172,
"executionDelayMs": 0,
"approximatePeakMemConsumptionKB": 8734,
"commandType": 27,
"queryDialect": 3,
"queryResultRows": 152583
}
Query duration and CPU usage is reduced too, but as I said above this highlights how different DAX patterns can result in very different memory footprints. Filtering on just the column you want to filter, rather than the whole table, is a lot more memory efficient! If you’re getting the “This visual has exceeded the available resources” error because you’ve hit the Query Memory Limit then it’s worth checking to see if you have used this antipattern in your measures and rewriting accordingly.
Footnote: it’s worth mentioning that two other variations on the rewritten measure, one using the Property Type column on the Property Type dimension table instead of the Property Type column on the fact table:
Detached Count V3 =
CALCULATE (
COUNTROWS ( 'Property Transactions' ),
'Property Type'[Property Type] = "D"
)
…and one that added the KeepFilters() function (which is commonly used in this type of measure):
Detached Count V4 =
CALCULATE (
COUNTROWS ( 'Property Transactions' ),
KEEPFILTERS ( 'Property Type'[Property Type] = "D" )
)
…both had the same memory usage as the V2 version of the measure, 9MB.




















































