Мы разобрали два контекста: строки (текущая строка в итераторе) и фильтра (что видно мере). В прошлом уроке прозвучало: меры игнорируют контекст строки. Но тогда почему AVERAGEX(VALUES(Продажи[НомерЗаказа]), [Выручка]) считает выручку по каждому заказу? Ответ — переход контекста. Это и есть мост между двумя контекстами.
В чём идея
Переход контекста (context transition) — это превращение текущей строки в эквивалентный фильтр. Его запускает CALCULATE — а также любая мера (потому что мера неявно обёрнута в CALCULATE).
Когда CALCULATE срабатывает внутри контекста строки, он берёт текущую строку и накладывает её как фильтр: «оставь только эту строку (и всё, что с ней связано)». Дальше выражение считается уже в этом новом контексте фильтра.
CALCULATE (и любая мера внутри итератора) превращает «текущую строку» в фильтр. Контекст строки → контекст фильтра. Поэтому мера, оказавшись в итераторе, вдруг начинает считаться «по строке».
Почему средний чек работает
Вернёмся к примеру:
Средний чек =
AVERAGEX ( VALUES ( Продажи[НомерЗаказа] ), [Выручка] )
Пошагово:
VALUESдаёт список уникальных заказов;AVERAGEXидёт по ним — это контекст строки (текущий заказ);- встречается мера
[Выручка]— она неявноCALCULATE, поэтому срабатывает переход контекста: текущий заказ становится фильтром; [Выручка]считается только по этому заказу;AVERAGEXусредняет суммы заказов.
Без перехода контекста [Выручка] вернула бы общий итог в каждой итерации, и среднее было бы бессмысленным. Именно переход делает расчёт правильным.
Вычисляемый столбец и CALCULATE
Тот же эффект в вычисляемом столбце. Просто [Выручка] в столбце игнорирует строку (вернёт общий итог). А обёрнутая в CALCULATE — «прижимается» к строке:
Выручка по этой строке = -- расчётный столбец
CALCULATE ( [Выручка] )
CALCULATE без фильтров, но в контексте строки, делает переход: значение считается для текущей строки. Это классический приём, когда нужно «материализовать» меру по строке таблицы.
Чем это объясняет «несходящийся итог»
Помните, что итог в таблице иногда не равен сумме строк? Часть таких случаев — как раз переход контекста: в каждой строке мера считается со своим «строчным» фильтром, а в «Итого» — без него. Для суммы совпадёт, для среднего/доли — нет. Это не баг, а следствие того, как строка превращается в фильтр.
Каждая итерация с мерой внутри запускает CALCULATE и переход контекста — на больших таблицах это ощутимая нагрузка. SUMX(Продажи, [КакаяТоМера]) на миллионах строк может быть медленным. Если можно посчитать без меры внутри итератора (на столбцах) — так дешевле.
Когда это включается — короткий список
Переход контекста срабатывает, если одновременно есть контекст строки и вызов CALCULATE (явный или неявный через меру):
- мера внутри итератора (
SUMX(T, [Мера])); CALCULATE(...)в вычисляемом столбце;- мера, ссылающаяся на другую меру внутри
ADDCOLUMNS/SUMMARIZE.
Нет контекста строки (обычная мера в визуале) — перехода нет, считается как есть.
Что дальше
Три кита DAX собраны: контекст строки, контекст фильтра и переход между ними. На этом фундаменте держится всё остальное. Дальше в курсе — фильтрующие функции, логика, связи и работа со временем.