Power BI и язык DAX являются удобными инструментами аналитиков. В DAX важно учитывать гранулярность — уровень детализации, который зависит от текущего контекста фильтров и группировки.

Мы привыкли работать с гранулярностью, и если в транзакционной системе проблемы с гранулярностью могут быть относительно незаметны, то в BI системах проблемы гранулярности сразу влияют на дашборды. Это усугубляется поведением движков BI систем, в которых гранулярность таблицы считается динамически в зависимости от выражения — как в Power BI.

При использовании некоторых функций, например, REMOVEFILTERS, снижение гранулярности может приводить к интуитивно непонятным результатам и считаться плохой практикой. Интересующимся особенностями снижения гранулярности на примере REMOVEFILTERS — добро пожаловать под кат :)

Общепринятое понятие гранулярности

Гранулярность — это уровень детализации данных. Чем выше гранулярность, тем более «мелкие» элементы представлены (например, транзакции). Чем ниже — тем более агрегированные данные (например, месяцы или категории). В реляционных моделях гранулярность тесно связана с нормальными формами и ключами таблиц. Можно привести несколько определений гранулярности.

  • Coursera — What Is Data Granularity? (2025):  «Data granularity measures how finely data is divided within a data structure. Choosing the right level of data granularity is essential to ensure that your data analysis and predictions are accurate…»

  • Valiotti Blog — What Is Data Granularity? (2025):  «Data granularity refers to the level of detail or precision of data. At a fine granularity (high detail), data is broken into many small pieces… At a coarse granularity (low detail), data is aggregated into larger summaries.»

Рассмотрим на примере таблицы продуктов product: можно считать, что в такой таблице product высокая гранулярность (если учесть, что есть таблица продаж sales, которая содержит продажи продуктов).

product_id

product_name

product_brand

product_color

1

Red Pen

The First

Red

2

Blue Pen

The First

Blue

3

Black Pen

The First

Black

4

Black Pen

The Best

Black

5

Red Pen

The Best

Red

Если убрать столбцы brand и color , то гранулярность таблицы product станет ниже:

product_id

product_name

1

Red Pen

2

Blue Pen

3

Black Pen

4

Black Pen

5

Red Pen

Более того, т.к. нет информации по бренду, то создается впечатление, что можно нормализовать таблицу и оставить только 3 записи: "Red Pen", "Blue Pen", "Black Pen", что может не соответствовать действительности, если бренды все же учитываются.

Таким образом, неформальная характеристика низкой гранулярности и проблем гранулярности — это когда видно, что в таблице «можно что‑то нормализовать».

Гранулярность в Power BI

В Power BI гранулярность таблиц изменяется движком во время расчета DAX выражения, например, из SQLBI — Working below a DAX formula’s granularity: «We described the granularity of a formula as being the level of detail that the formula uses to compute its result.»

Гранулярность в DAX определяется:

  • структурой таблицы,

  • текущим контекстом фильтра,

  • выражениями, которые создают или снимают фильтры.

Таким образом, меры вычисляются на уровне гранулярности, заданном контекстом фильтра и модифицирующими функциями CALCULATE , FILTER , REMOVEFILTERS,  ALL и другими.

Пример: исходная таблица без изменений

Рассмотрим схему с одной таблицей фактов sales и одним справочником продуктов product.

Таблица product содержит поля ID продукта, имя продукта, бренд и цвет.

Также есть таблица фактов с продажами продуктов sales.

Рассмотрим запрос с группировкой по бренду и цвету продукта.

Далее применим фильтрацию по цвету продукта — возьмем красные продукты — и рассмотрим варианты отмены контекста этого фильтра при помощи REMOVEFILTERS.

REMOVEFILTERS по всем столбцам таблицы соответствует REMOVEFILTERS по всей таблице

Рассмотрим сразу предельный случай — REMOVEFILTERS по всем столбцам, который соответствует REMOVEFILTERS по таблице.

EVALUATE
	SUMMARIZECOLUMNS(
		'product'[product_brand],
		'product'[product_color],
		FILTER(
			'product',
			'product'[product_color] = "Red"
		),
		"Сумма с отменой фильтра по всем столбцам", CALCULATE(
			SUM(sales[amount]),
			REMOVEFILTERS(
				'product'[product_id],
				'product'[product_name],
				'product'[product_brand],
				'product'[product_color]
			)
		),
		"Сумма с отменой фильтра по таблице", CALCULATE(
			SUM(sales[amount]),
			REMOVEFILTERS('product')
		)
	)

Видно, что результаты для отмены фильтра по всем столбцам и отмены фильтра по таблице совпадают.

Таким образом,REMOVEFILTERS по всем столбцам и REMOVEFILTERS по таблице представляют собой одно и то же, не снижают гранулярность и их результаты совпадают.

REMOVEFILTERS по одному и двум столбцам без снижения гранулярности

Предельный случай — REMOVEFILTERS по всем столбцам — уже рассмотрен, теперь будем добавлять в REMOVEFILTERS по одному столбцу, чтобы показать, что это не снимает FILTER по красному цвету из SUMMARIZECOLUMNS до тех пор, пока не снизилась гранулярность таблицы product.

Рассмотрим отмену контекста фильтра по цвету.

EVALUATE
	SUMMARIZECOLUMNS(
		'product'[product_brand],
		'product'[product_color],
		FILTER(
			'product',
			'product'[product_color] = "Red"
		),
		"REMOVEFILTERS по brand", CALCULATE(
			SUM(sales[amount]),
			REMOVEFILTERS('product'[product_color])
		)
	)

В данном случае снимается фильтр только по одному столбцу color, и гранулярность по другим полям сохраняется из-за наличия ID и имени.

В итоге влияние FILTER по красному цвету из SUMMARIZECOLUMNS сохраняется.

Рассмотрим REMOVEFILTERS по двум столбцам: цвет и имя продукта.

EVALUATE
	SUMMARIZECOLUMNS(
		'product'[product_brand],
		'product'[product_color],
		FILTER(
			'product',
			'product'[product_color] = "Red"
		),
		"REMOVEFILTERS по color и name", CALCULATE(
			SUM(sales[amount]),
			REMOVEFILTERS(
				'product'[product_color],
				'product'[product_name]
			)
		)
	)

Гранулярность сохраняется на уровне других полей таблицы (ID и бренд продукта) и не снижается, влияние FILTER по красному цвету из SUMMARIZECOLUMNS сохраняется.

Снижение гранулярности при REMOVEFILTERS по трем столбцам

Снимем фильтры по трем столбцам, например, ID продукта, имя и цвет, и покажем, что это приведет к отмене влияния FILTER по красному цвету из SUMMARIZECOLUMNS и снижению гранулярности.

EVALUATE
	SUMMARIZECOLUMNS(
		'product'[product_brand],
		'product'[product_color],
		FILTER(
			'product',
			'product'[product_color] = "Red"
		),
		"REMOVEFILTERS по id, color, name", CALCULATE(
			SUM(sales[amount]),
			REMOVEFILTERS(
				'product'[product_id],
				'product'[product_color],
				'product'[product_name]
			)
		)
	)

Видим, что результаты изменились и влияние FILTER по красному цвету из SUMMARIZECOLUMNS отменилось.

Видим, что в красные "Red" продажи попали продажи по всем цветам, т.е., например, для бренда "The First" выводятся продажи для "Red", "Blue" и "Black", т.е. 33.03 + 20.10 + 32.10 = 85.23.

Из-за снижения гранулярности FILTER по цвету из SUMMARIZECOLUMNS снялся, причем отображаются несуществующие данные — при цвете "Red" выводятся продажи по другим цветам.

При REMOVEFILTERS по ID, имени и цвету таблица product для движка DAX становится следующей:

product_id

product_name

product_brand

product_color

1

Red Pen

The First

Red

2

Blue Pen

The First

Blue

3

Black Pen

The First

Black

4

Black Pen

The Best

Black

5

Red Pen

The Best

Red

Теперь строки с ID, равным 1, 2 и 3, в движке Power BI считаются «одинаковыми» и «красными» под брендом "The First", и строки c ID, равным 4 и 5, считаются «одинаковыми» и «красными» под брендом "The Best", хотя видно, что цвета разные, т.е. из-за снижения гранулярности выводятся несуществующие данные. Насколько это полезно для дашборда (или нет) — решать аналитику, но выглядит неочевидно и в общем и целом считается скорее негативной практикой.

Это приводит к тому, что:

  • несколько исходных строк «сливаются» в одну;

  • отображаются агрегированные данные по продажам, которых не существует в таблице фактов;

  • логика становится интуитивно непонятной.

При этом можно отметить, что сам REMOVEFILTERS не снижает гранулярность, и она снижается из-за самого движка Power BI при отсутствии доступных столбцов для различения строк таблицы product.

Отношение аналитиков DAX к снижению гранулярности

В SQLBI — Working below a DAX formula’s granularity видим, что подчеркиваются риски получения результатов, которых нет в данных: «If you work at a granularity lower than the one supported by the formula, you might obtain numbers that do not exist in the data.»

Также важно разумное отношение к отмене контекста фильтра, что можно интерпретировать как отсутствие избыточных столбцов в REMOVEFILTERS: SQLBI — Filter Arguments in CALCULATE: «Finding the right granularity for [a filter argument] is important to control the result and the performance.»

Вывод

Таким образом, решение по использованию REMOVEFILTERS по нескольким столбцам со снижением гранулярности остается на усмотрение аналитика, однако в этом случае нужно понимать риски получения результатов, отсутствующих в исходных данных, и иметь в виду неочевидность подобных DAX мер, что усложняет их понимание и поддержку. Разумным выглядит снятие фильтров по тем столбцам, где это действительно нужно, что обеспечивает корректные результаты и помогает упростить логику модели.

Надеюсь, эти наблюдения могут быть полезными, успешных дашбордов! :)