Цель экзамена (Model the data, 25–30 %): Use the CALCULATE function. Связанные: Create single aggregation measures и Identify use cases for calculated columns (u10), Implement time intelligence measures (u12), Create calculation groups (u13).
1. Боль: мера, которая выглядит как SQL и лжёт
Руководитель консалтингового направления Aldervane хочет одну матрицу: часы по клиентам, оплачиваемые часы, долю клиента во всех часах и часы по клиентам из Великобритании. Томаш, старший разработчик, который десять лет пишет на T-SQL, пишет меры так, как написал бы WHERE:
Billable Hours = CALCULATE ( [Hours], Timesheet[Billable] = "Y" )
UK Hours = CALCULATE ( [Hours], Client[Country] = "UK" )
На его первой странице каждое число выглядит правильным. Потом коллега перетаскивает Billable на строки, а Country — на столбцы, и матрица начинает показывать 28 оплачиваемых часов в строке неоплачиваемых и 26 британских часов в столбце Ireland. Ничего не сломано. Томаш прочитал CALCULATE как «добавь условие». На самом деле это значит «вычисли это выражение в изменённом контексте фильтра», и изменение обычно заменяет фильтры, а не добавляется к ним.
Этот юнит — об этом единственном различии. Как только вы можете назвать контекст фильтра ячейки, каждый вопрос о CALCULATE на экзамене превращается в арифметику.
2. Модель, которую мы считаем в уме
Описание
Two boxes joined by an arrow from left to right. The left box is the dimension table Client with the columns ClientName, which is the key, and Country. The right box is the fact table Timesheet with the columns ClientName, Billable with values Y or N, and Hours. The arrow is labelled one to many, single direction: a filter on Client flows to Timesheet, never the other way. All examples in this unit use this model.
Client
| ClientName | Country |
|---|---|
| Contoso Energy | UK |
| Fabrikam Logistics | Ireland |
| Woodgrove Bank | UK |
Timesheet
| ClientName | Billable | Hours |
|---|---|---|
| Contoso Energy | Y | 10 |
| Contoso Energy | N | 2 |
| Fabrikam Logistics | Y | 6 |
| Fabrikam Logistics | Y | 4 |
| Woodgrove Bank | Y | 8 |
| Woodgrove Bank | N | 6 |
Базовая мера — Hours = SUM ( Timesheet[Hours] ). Итоги, которые стоит держать в голове: Contoso 12, Fabrikam 10, Woodgrove 14, все клиенты 36; оплачиваемые часы 28; клиенты из Великобритании 26.
3. Контекст фильтра одним предложением (из u10)
Каждая ячейка визуального элемента вычисляется отдельно. Её контекст фильтра — это набор фильтров из заголовка строки, заголовка столбца, срезов, панели фильтров и RLS. В матрице с ClientName на строках ячейка Contoso имеет фильтр Client[ClientName] = "Contoso Energy", и этот фильтр проходит через связь к Timesheet. [Hours] там равно 12. У строки итога нет фильтра по ClientName, поэтому она показывает 36.
Привычка для экзамена: прежде чем что-то считать, запишите контекст фильтра ячейки. Затем примените к нему то, что делает CALCULATE.
4. CALCULATE задаёт фильтры — и заменяет их
CALCULATE ( <expression>, <filter1>, <filter2>, … )
Аргумент-фильтр вроде Timesheet[Billable] = "Y" — сокращённая запись таблицы с одним столбцом и допустимыми значениями:
-- what the engine actually uses
FILTER ( ALL ( Timesheet[Billable] ), Timesheet[Billable] = "Y" )
Обратите внимание на ALL. Сокращённая запись игнорирует любой фильтр, уже существующий на Timesheet[Billable], и ставит на его место свой собственный. Фильтры на других столбцах остаются как есть.

Описание
Two square charcoal frames on beige paper, joined by a thick arrow pointing from left to right. The left frame, labelled VISUAL, holds rows of faded coin outlines; only the top row of four slate-blue coins is solid — the selection the visual's filter lets through. The right frame, labelled CALCULATE, holds the same faded coins, but now only the bottom row of four ochre coins is solid. The idea: CALCULATE does not narrow the visual's selection further — it swaps the filter on that column for its own, so a different set of rows is counted. The picture does not show filters on other columns, which CALCULATE leaves in place.
4.1 Когда замена — именно то, что нужно
Матрица с ClientName на строках:
| ClientName | Hours | Billable Hours |
|---|---|---|
| Contoso Energy | 12 | 10 |
| Fabrikam Logistics | 10 | 10 |
| Woodgrove Bank | 14 | 8 |
| Итог | 36 | 28 |
Фильтр строки стоит на ClientName, а CALCULATE фильтрует Billable. Это разные столбцы, поэтому два фильтра объединяются, и числа получаются такими, каких ожидал Томаш.
4.2 Когда это застаёт врасплох
Поставьте на строки вместо этого Timesheet[Billable]:
| Billable | Hours | Billable Hours |
|---|---|---|
| N | 8 | 28 |
| Y | 28 | 28 |
| Итог | 36 | 28 |
В строке N фильтр ячейки — Billable = "N". CALCULATE заменяет его на Billable = "Y", поэтому ячейка показывает 28. Ловушка экзамена: любой вопрос, где на оси стоит тот же столбец, который фильтрует мера, проверяет, знаете ли вы это.
То же происходит с UK Hours = CALCULATE ( [Hours], Client[Country] = "UK" ):
| Ось на строках | Строка | UK Hours | Почему |
|---|---|---|---|
| ClientName | Contoso Energy | 12 | фильтр ClientName остаётся, добавлено Country = UK: Contoso — в UK |
| ClientName | Fabrikam Logistics | (пусто) | ClientName = Fabrikam и Country = UK: таких строк нет |
| ClientName | Woodgrove Bank | 14 | оба фильтра выполняются |
| Country | Ireland | 26 | Country = Ireland заменён на Country = UK |
| Country | UK | 26 | заменён тем же значением |
Строка Fabrikam пуста, а строка Ireland показывает 26, хотя Fabrikam — ирландский клиент. Вся разница в том, какой столбец фильтрует визуальный элемент.
4.3 Пересечение вместо замены: KEEPFILTERS
Если требование — «часы по UK, но с учётом того, что пользователь уже выбрал», оберните фильтр:
UK Hours (keep) = CALCULATE ( [Hours], KEEPFILTERS ( Client[Country] = "UK" ) )
KEEPFILTERS пересекает новый фильтр с существующим на том же столбце. С Country на строках: Ireland → пусто, UK → 26.
Табличный фильтр тоже даёт пересечение, потому что перебирает только уже видимые строки:
UK Hours (table) = CALCULATE ( [Hours], FILTER ( Client, Client[Country] = "UK" ) )
Это работает, но медленнее и менее точно, чем KEEPFILTERS: фильтруется каждый столбец Client, а не только Country. Используйте FILTER, когда условию нужно то, чего не выразить простым предикатом, — чаще всего меру (раздел 6).
5. Снятие фильтров: доля от итога
Hours All Clients = CALCULATE ( [Hours], REMOVEFILTERS ( Client ) )
% of All Clients = DIVIDE ( [Hours], [Hours All Clients] )
| ClientName | Hours | Hours All Clients | % of All Clients |
|---|---|---|---|
| Contoso Energy | 12 | 36 | 33.3 % |
| Fabrikam Logistics | 10 | 36 | 27.8 % |
| Woodgrove Bank | 14 | 36 | 38.9 % |
Функции, которые снимают фильтры, и когда выбирать каждую:
| Функция (как аргумент CALCULATE) | Снимает | Оставляет | Типичное применение |
|---|---|---|---|
REMOVEFILTERS ( Client ) |
фильтры на каждом столбце Client | всё остальное | доля от всей фирмы |
ALL ( Client ) |
то же, что REMOVEFILTERS, когда используется как модификатор | всё остальное | старый код; ALL также может возвращать таблицу, REMOVEFILTERS — нет |
REMOVEFILTERS ( Client[ClientName] ) |
фильтр только на одном столбце | другие столбцы Client, например Country | доля внутри страны |
ALLEXCEPT ( Client, Client[Country] ) |
все фильтры Client, кроме Country | Country | то же самое, записанное наоборот |
ALLSELECTED ( Client ) |
фильтры, которые применяет сам визуальный элемент | фильтры от срезов и внешнего отчёта | «доля от того, на что смотрит пользователь» |
Ловушка экзамена: «Доли должны по-прежнему давать в сумме 100 %, когда пользователь выбирает UK в срезе» означает ALLSELECTED. С REMOVEFILTERS британские клиенты дали бы в сумме лишь 72 % (26 / 36), потому что знаменатель игнорировал бы срез.
6. Фильтрация по мере
Булев аргумент-фильтр может сравнивать столбцы одной таблицы с константами, но не может ссылаться на меру. «Оплачиваемые часы клиентов, у которых всего больше 10 часов» требуют таблицы клиентов для перебора:
Billable Hours Big Clients =
CALCULATE (
[Billable Hours],
FILTER ( VALUES ( Client[ClientName] ), [Hours] > 10 )
)
VALUES ( Client[ClientName] ) — маленькая таблица: по одной строке на каждого видимого клиента. Для каждой строки FILTER вычисляет [Hours] — это мера, и поэтому она вызывает переход контекста (следующий раздел). Перебирайте самую маленькую таблицу, которая отвечает на вопрос, — столбец измерения, никогда не таблицу фактов.
7. Порядок вычисления CALCULATE
Описание
Five boxes stacked top to bottom and joined by arrows. One: the filter arguments are evaluated in the original filter context. Two: if there is a row context, context transition turns the current row into filters. Three: modifiers such as REMOVEFILTERS, ALL, USERELATIONSHIP and CROSSFILTER are applied. Four: the filter arguments are applied, replacing existing filters on the same columns, or intersecting with them when wrapped in KEEPFILTERS. Five: the expression is evaluated in the resulting filter context. The order explains why a filter argument wins over a filter that context transition created on the same column.
На экзамене порядок нужен редко. Он нужен, когда два шага касаются одного и того же столбца, например аргумент-фильтр на столбце, который также отфильтровал переход контекста: шаг 4 идёт после шага 2, поэтому побеждает аргумент-фильтр.
8. Переход контекста

Описание
On the left, a column of five person pictograms stands for the rows of a table, labelled ROW; the middle person is brick red and framed. An arrow carries this one row into a black funnel. From the narrow end of the funnel an arrow enters a frame labelled FILTER, which holds nine coins in three rows: only the middle row of three brick-red coins is solid, the rest are faded. The idea: context transition turns the row being iterated into a filter, so the calculation sees only the fact rows that belong to that row. The picture does not show how many columns of the row become filters — in DAX, all of them do.
Контекст строки существует в вычисляемых столбцах и в итераторах (SUMX, AVERAGEX, FILTER…). Он означает «я нахожусь на этой строке», но ничего не фильтрует. CALCULATE его преобразует: каждый столбец текущей строки становится фильтром. Это преобразование и есть переход контекста.
Три вычисляемых столбца на Client:
| Вычисляемый столбец | Contoso | Fabrikam | Woodgrove |
|---|---|---|---|
= SUM ( Timesheet[Hours] ) |
36 | 36 | 36 |
= CALCULATE ( SUM ( Timesheet[Hours] ) ) |
12 | 10 | 14 |
= [Hours] |
12 | 10 | 14 |
- У первого столбца нет контекста фильтра, поэтому SUM видит весь Timesheet. Это классическая ошибка SQL-мышления: разработчик ожидает коррелированный подзапрос, а получает общий итог.
- Второй столбец использует CALCULATE без аргументов-фильтров. Его единственная задача здесь — переход контекста.
- Третий ссылается на меру. Ссылка на меру всегда несёт неявный CALCULATE, поэтому тоже вызывает переход контекста.
То же правило действует внутри мер, которые перебирают строки:
Avg Hours per Client (right) = AVERAGEX ( Client, [Hours] ) -- total row: 12
Avg Hours per Client (wrong) = AVERAGEX ( Client, SUM ( Timesheet[Hours] ) ) -- total row: 36
В неправильной версии каждая итерация суммирует все 36 часов (перехода нет), и среднее из 36, 36 и 36 равно 36.
Ловушка экзамена: «вычисляемый столбец возвращает одно и то же значение в каждой строке» указывает на отсутствующий переход контекста. Исправление — CALCULATE или ссылка на меру.
9. Цена
- FILTER по таблице фактов работает медленно.
CALCULATE ( [Hours], FILTER ( Timesheet, Timesheet[Billable] = "Y" ) )перебирает каждую видимую строку табеля и может быть на порядки медленнее предиката по столбцу. На таблице фактов в 10 миллионов строк вы увидите это в Performance Analyzer (u14). - Переход контекста внутри больших итераторов дорог.
SUMX ( Timesheet, [Some Measure] )выполняет переход для каждой строки фактов. Перебирайте вместо этого измерение илиVALUESстолбца. - Переход фильтрует каждый столбец строки. Если в таблице есть дублирующиеся строки, переход фильтрует все одинаковые строки сразу. Перебор таблицы без ключа даёт молча неверные результаты.
- Читаемость. Каждый заменённый фильтр невидим в отчёте. Пользователь, который видит 28 в строке «N», не догадается почему. Называйте такие меры честно (
Billable Hours (ignores Billable filter)) или используйтеKEEPFILTERS, если должен побеждать фильтр визуального элемента.
| Интуиция SQL | Что на самом деле делает DAX |
|---|---|
WHERE Billable = 'Y' добавляет условие |
фильтр CALCULATE заменяет фильтр на этом столбце |
SUM(Hours) OVER () для общего итога |
CALCULATE ( [Hours], REMOVEFILTERS ( … ) ) |
| коррелированный подзапрос на каждую строку | вычисляемому столбцу нужен переход контекста, чтобы увидеть «свои» строки |
HAVING SUM(Hours) > 10 |
FILTER ( VALUES ( … ), [Hours] > 10 ) внутри CALCULATE |
10. Лабораторная — CALCULATE на датасете Aldervane
Вам понадобятся: Power BI Desktop (бесплатный, Windows) и публичный датасет aldervane-pbi-dataset (файлы CSV плюс README со связями). Время: около 40 минут. Стоимость: нет.
- Get data → Text/CSV для
DimClient,DimEngagementиFactTimesheet. В Model view создайтеDimClient[ClientKey] 1 → * DimEngagement[ClientKey]иDimEngagement[EngagementKey] 1 → * FactTimesheet[EngagementKey], обе однонаправленные. - Создайте меры:
Hours = SUM ( FactTimesheet[Hours] ) Billable Hours = CALCULATE ( [Hours], FactTimesheet[Billable] = "Y" ) UK Hours = CALCULATE ( [Hours], DimClient[Country] = "UK" ) UK Hours (keep) = CALCULATE ( [Hours], KEEPFILTERS ( DimClient[Country] = "UK" ) ) Hours All Clients = CALCULATE ( [Hours], REMOVEFILTERS ( DimClient ) ) % of All Clients = DIVIDE ( [Hours], [Hours All Clients] ) - Откройте DAX query view и выполните:
Ожидается: Ireland 9 664 / 18 907 / пусто; Poland 6 186 / 18 907 / пусто; UK 18 907 / 18 907 / 18 907. Объясните каждое число вслух, прежде чем смотреть.EVALUATE SUMMARIZECOLUMNS ( DimClient[Country], "Hours", [Hours], "UK Hours", [UK Hours], "UK Hours (keep)", [UK Hours (keep)] ) - Постройте матрицу с
DimClient[ClientName]на строках и[Hours],[% of All Clients]. Contoso Energy должен показать 19.5 %. Добавьте срез поCountry, выберите UK и посмотрите, как доли перестают давать в сумме 100 %. Поменяйте знаменатель наALLSELECTED ( DimClient )и проверьте снова. - На
DimEngagementдобавьте два вычисляемых столбца:Hours (bare) = SUM ( FactTimesheet[Hours] )иHours (CT) = [Hours]. Для ENG-4401 ожидайте 34 757 и 1 567.5. - Общий итог — 34 757 часов как загружено: эта лабораторная загружает CSV заново, поэтому намеренная запись на −3 часа всё ещё внутри. Очистка в u04 её удаляет (после неё 34 760 часов); числа лабораторной выше — как загружено.
Нет машины с Windows или Desktop заблокирован? Пройдите шаги 2–5 на бумаге, используя ожидаемые числа выше. Любое практическое задание этого юнита можно решить без Power BI.
11. Ловушки экзамена — итог
- Аргумент-фильтр заменяет фильтр на том же столбце; другие столбцы сохраняют свои фильтры.
- Тот же столбец на оси и в аргументе-фильтре → каждая строка показывает одно и то же число.
- «С учётом выбора пользователя» →
KEEPFILTERS(или табличный фильтр). - «Доля от всего» →
REMOVEFILTERS/ALL; «доля от выбранного» →ALLSELECTED; «доля внутри страны» → снять только фильтр ClientName /ALLEXCEPT. - Булев фильтр не может использовать меру →
FILTER ( VALUES ( … ), [Measure] > x ). - Вычисляемый столбец показывает общий итог в каждой строке → отсутствует переход контекста; используйте CALCULATE или ссылку на меру.
- Ссылка на меру = неявный CALCULATE.
12. Запомните картинки
Перескажите каждую картинку одним предложением. Две рамки: CALCULATE подменяет фильтр на столбце, а не сужает его. Воронка: текущая строка становится фильтром. Если вы можете сказать оба, не глядя, у вас в руках суть этой цели экзамена.
Источники: Microsoft Learn — "CALCULATE function (DAX)", "KEEPFILTERS function (DAX)", "ALLSELECTED function (DAX)", "Modify DAX filter context in semantic models" (training module, unit "Perform context transition"), "DAX query view". Exam PL-300 study guide, skills measured as of April 20, 2026.
