Rules / DAX Expressions

Filter measure values by columns, not tables

warning FILTER_MEASURE_VALUES_BY_COLUMNS · ported · model layer · scope: Measure, CalculatedColumn, CalculationItem

What it checks

CALCULATE or CALCULATETABLE whose first filter argument is FILTER('Table', [Measure] ...).

Each finding names the object that holds the expression: a measure as [Best Seller Sales], a calculated column as 'Sales'[Best Seller Amount], a calculation item by its name with its calculation group in the detail.

Example

Fires the rule
table Sales
	column Amount
		dataType: decimal
		sourceColumn: Amount
	column 'Product ID'
		dataType: int64
		sourceColumn: ProductID
	measure 'Total Sales' = SUM(Sales[Amount])
		formatString: #,0
	measure 'Best Seller Sales' = CALCULATE([Total Sales], FILTER('Product', [Total Sales] > 1000))
		formatString: #,0

table Product
	column 'Product ID'
		dataType: int64
		isKey
		sourceColumn: ProductID
	column Category
		dataType: string
		sourceColumn: Category

relationship Sales_Product
	fromColumn: Sales.'Product ID'
	toColumn: Product.'Product ID'
After the fix
table Sales
	column Amount
		dataType: decimal
		sourceColumn: Amount
	column 'Product ID'
		dataType: int64
		sourceColumn: ProductID
	measure 'Total Sales' = SUM(Sales[Amount])
		formatString: #,0
	measure 'Best Seller Sales' = CALCULATE([Total Sales], FILTER(VALUES('Product'[Product ID]), [Total Sales] > 1000))
		formatString: #,0

table Product
	column 'Product ID'
		dataType: int64
		isKey
		sourceColumn: ProductID
	column Category
		dataType: string
		sourceColumn: Category

relationship Sales_Product
	fromColumn: Sales.'Product ID'
	toColumn: Product.'Product ID'

Why it matters

FILTER over a whole table evaluates the measure once per row of the table. Over a fact table that is millions of measure evaluations to keep a few rows. Filtering the distinct values of one column instead evaluates the measure once per value, usually thousands of times fewer, and produces the same rows.

How to fix it

Iterate the column the answer actually varies by, not the table:

Best Seller Sales = CALCULATE ( [Total Sales], FILTER ( VALUES ( 'Product'[Product ID] ), [Total Sales] > 1000 ) )

VALUES respects the filter already on the column; swap in ALL to ignore it. Pick the column with the fewest distinct values that still gives the right answer, and note that passing a column also narrows what the filter carries into CALCULATE, one column's values rather than whole rows of the table, so the rewrite pays even where the column you iterate is the table's key. In Power BI Desktop, select the measure in the Data pane and edit it in the formula bar; a calculated column and a calculation item are edited the same way. In the TMDL file, edit the expression after measure 'Best Seller Sales' =, after column Name = for a calculated column, or after calculationItem Name = in the calculation group.

When to ignore it

Where the measure's value depends on more than one column of the table, no single column's distinct values reproduce the row set, and iterating the table is the honest form. Sales per product and store is the usual shape: iterate both columns together rather than the table if you can, and leave the finding alone if you cannot. The finding is also worth less over a small dimension, where the table has a few hundred rows and the column a few dozen distinct values, than over a fact table.

To ignore this rule on one object, add annotation pbiplint.ignore = FILTER_MEASURE_VALUES_BY_COLUMNS under the object in its TMDL file. Power BI Desktop keeps the annotation. To turn the rule off for a whole project, set "FILTER_MEASURE_VALUES_BY_COLUMNS": "off" under rules in pbiplint.config.json.

Quirks

Ported from Microsoft's Best Practice Analyzer ruleset (opens in a new tab).

Check a model for this Improve this page (opens in a new tab)