Контексты вычисления в DAX: Строка, Запрос и Фильтр – Сердце Ваших Аналитических Возможностей
Привет, будущий мастер данных!

Если вы когда-либо задавались вопросом, почему одна и та же формула в DAX может давать разные результаты в зависимости от того, куда вы ее поместите в отчете, или почему она вдруг "знает", как отфильтровать данные по выбранному вами региону, то вы на пороге самого важного открытия в DAX.
Это не магия, хотя поначалу может так показаться. Это контекст вычисления.

Представьте себе, что вы — детектив, а ваши данные AdventureWorks — это огромное место преступления.
Контекст — это как ваша лупа, которая позволяет вам сфокусироваться на определенной части этого места.
Иногда вы смотрите на каждую отдельную улику (контекст строки), иногда вы сужаете область поиска до определенного района (контекст фильтра), а иногда вы просто ждете общего ответа от всей команды (контекст запроса).
Понимание этих "луп" — это ключ к раскрытию всех тайн, скрытых в ваших данных.

Эта статья — ваш путеводитель по самым фундаментальным концепциям DAX.
Я обещаю, что мы разберем их так, чтобы даже "чайник" понял, как работает эта магия, и захотел изучать DAX дальше. Вложите в чтение столько же сил, сколько я вложил в написание, и вы не пожалеете!
Часть 1: Контекст Строки (Row Context) – Взгляд на Каждую Деталь

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

Что это такое?
Контекст строки (Row Context) — это временный "фокус" DAX на одной конкретной строке таблицы. Когда формула вычисляется в контексте строки, DAX "знает" значения всех столбцов этой конкретной строки. Он не видит другие строки в таблице, только ту, над которой работает в данный момент.

Когда активируется Контекст Строки?
Контекст строки активируется в двух основных случаях:
1.     При создании Вычисляемых Столбцов (Calculated Columns): Это самый очевидный пример.
Когда вы добавляете новый вычисляемый столбец в таблицу, DAX проходит по каждой строке этой таблицы,
вычисляя формулу для каждой из них.
○       Пример с AdventureWorks: Вы хотите добавить столбец Gross Profit Per Order в таблицу FactInternetSales.
Gross Profit Per Order = FactInternetSales[SalesAmount] - FactInternetSales[TotalProductCost]

Когда DAX вычисляет эту формулу для первой строки, он берет SalesAmount и TotalProductCost из первой строки.
Для второй строки — из второй строки, и так далее.

2.     Внутри функций-итераторов (Iterators): Это функции, которые заканчиваются на X (например, SUMX, AVERAGEX, MAXX, MINX, COUNTX, RANKX, FILTER). Эти функции итерируют по каждой строке заданной таблицы, выполняя для каждой строки определенное выражение.
○       Пример с AdventureWorks: Вы хотите рассчитать общую сумму продаж только для продуктов, у которых скидка была больше 10%.
Total Sales High Discount Products =
SUMX (
   FILTER ( FactInternetSales, FactInternetSales[DiscountAmount] > 0.1 ),
   FactInternetSales[SalesAmount]
)

Здесь SUMX и FILTER создают контекст строки. FILTER итерирует по FactInternetSales. Для каждой строки FILTER проверяет условие FactInternetSales[DiscountAmount] > 0.1 в контексте этой строки. Затем SUMX итерирует по отфильтрованным строкам, и для каждой из них вычисляет FactInternetSales[SalesAmount] в контексте строки.

Важные моменты о Контексте Строки:
●       Построчное вычисление: Формула "видит" только текущую строку.
●       Статичность (для вычисляемых столбцов): Значения вычисляемых столбцов сохраняются в модели и не меняются, пока вы не обновите данные. Они не реагируют на фильтры, которые пользователь применяет в отчете.
●       Влияние на память: Вычисляемые столбцы увеличивают размер вашей модели данных, так как их значения хранятся.
Часть 2: Контекст Запроса (Query Context) – Общая Картина от Клиента

Контекст запроса — это наименее обсуждаемый, но важный аспект в DAX, потому что он тесно переплетается с контекстом фильтра. По сути, это то, как клиентское приложение (например, Power BI, Excel PivotTable) запрашивает данные у вашей модели DAX.

Что это такое?
Контекст запроса (Query Context) — это набор данных, который клиентское приложение запрашивает у модели DAX для отображения в визуализации. Когда вы перетаскиваете поля в таблицу, матрицу или график, клиент формирует "запрос" к модели, определяя, какие измерения и меры должны быть включены и как они должны быть сгруппированы.

Как это работает?
Представьте, что вы создаете таблицу в Power BI и перетаскиваете туда Product Category и меру Total Sales.
●       Клиент (Power BI) формирует запрос, который говорит: "Дай мне Total Sales, сгруппированные по Product Category".
●       DAX обрабатывает этот запрос, и для каждой Product Category (Bikes, Components и т.д.) он создает контекст фильтра.

Таким образом, контекст запроса — это высокоуровневая структура, которую клиент запрашивает,
а контекст фильтра — это то, как DAX детализирует и вычисляет значения для каждой части этого запроса.
В большинстве случаев, когда мы говорим о том, как DAX вычисляет меры, мы имеем в виду именно контекст фильтра.
Часть 3: Контекст Фильтра (Filter Context) – Динамический Фокус Вашего Анализа

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

Что это такое?
Контекст фильтра (Filter Context) — это набор фильтров, которые применяются к вашей модели данных перед тем, как мера будет вычислена. Эти фильтры действуют как "срез" по всей вашей модели, влияя на все связанные таблицы.

Как устанавливается Контекст Фильтра?
Контекст фильтра может быть установлен несколькими способами:

1.     Через элементы отчета (Implicit Filters):
○       Срезы (Slicers): Когда пользователь выбирает значение в срезе (например, Calendar Year = 2007), этот выбор создает фильтр, который применяется ко всей модели.
○       Фильтры на уровне страницы/отчета/визуализации: Фильтры, которые вы применяете в Power BI Desktop на панелях фильтров.
○       Оси визуализаций (Rows/Columns of a Matrix/Table): Когда вы помещаете Product Category в строки таблицы, каждая строка таблицы (например, 'Bikes') автоматически создает контекст фильтра, который применяется к мере Total Sales. Мера Total Sales для строки 'Bikes' будет вычислена только для данных, где Product Category = 'Bikes'.
○       Выделение (Highlighting): Когда вы кликаете по элементу на одной визуализации, это фильтрует другие визуализации на странице.

2.     Явно с помощью функций DAX (Explicit Filters):
○       Функция CALCULATE: Это король DAX и единственная функция, которая может изменять контекст фильтра. Мы рассмотрим ее подробно в следующей части.
○       Функции, возвращающие таблицы: Такие как FILTER, ALL, VALUES, RELATEDTABLE. Эти функции могут создавать или модифицировать таблицы, которые затем могут быть использованы для фильтрации.
Как работает Контекст Фильтра?Когда вы используете меру, DAX сначала собирает все активные фильтры со всех источников (срезы, визуализации, другие фильтры). Затем он применяет эти фильтры к вашей модели данных.
Только после этого мера вычисляется на основе отфильтрованных данных.

Пример с AdventureWorks: Динамические Продажи
Создадим простую меру:
Total Sales = SUM(FactInternetSales[SalesAmount])

1.     Без фильтров: Если вы просто поместите Total Sales в карточку Power BI, контекст фильтра будет пустым (нет активных фильтров). Мера вернет общую сумму продаж по всей компании AdventureWorks.

2.     С фильтром из среза: Добавьте срез по Date[Calendar Year] и выберите 2007.
○       Контекст фильтра теперь включает Date[Calendar Year] = 2007.
○       Мера Total Sales пересчитается и покажет общие продажи только за 2007 год.

3.     В матрице/таблице: Поместите Product[Category] в строки матрицы и Date[Calendar Year] в столбцы.
○       Для каждой ячейки матрицы DAX создает уникальный контекст фильтра.
○       Например, для ячейки на пересечении 'Bikes' и '2007':
■       Контекст фильтра будет включать Product[Category] = "Bikes" И Date[Calendar Year] = 2007.
■       Мера Total Sales вернет сумму продаж велосипедов за 2007 год.

Это и есть магия контекста фильтра: одна и та же мера динамически адаптируется к любому срезу данных, предоставляя релевантный результат. Меры не хранят значения; они вычисляются в зависимости от того, как вы "смотрите" на данные.
Часть 4: CALCULATE() – Король Манипуляции Контекстом Фильтра

Если контекст фильтра — это набор правил, по которым DAX смотрит на данные, то CALCULATE() — это ваша суперспособность, которая позволяет переписывать эти правила.
Это самая мощная и, возможно, самая важная функция в DAX. Освоив CALCULATE, вы откроете для себя безграничные возможности аналитики.

Зачем нужна CALCULATE()?
CALCULATE позволяет вам:
●       Удалять фильтры: Временно игнорировать существующие фильтры.
●       Добавлять новые фильтры: Применять дополнительные фильтры, которые не были активны.
●       Изменять существующие фильтры: Заменять активные фильтры на новые.

Это позволяет выполнять такие задачи, как:
●       Расчет процента от общего итога (игнорируя текущие фильтры).
●       Сравнение текущих продаж с продажами за предыдущий год/месяц.
●       Расчет продаж для определенной категории продуктов, независимо от того, что выбрал пользователь в срезе.

Как работает CALCULATE()?
CALCULATE работает в три основных этапа:
1.     Оценка выражения: Сначала CALCULATE оценивает Expression (обычно меру) в текущем контексте фильтра.

2.     Модификация контекста фильтра: Затем CALCULATE применяет свои собственные фильтры (Filter1, Filter2, ...).
○       Если фильтр в CALCULATE относится к столбцу, который уже отфильтрован в текущем контексте, фильтр в CALCULATE переопределяет (заменяет) существующий фильтр.
○       Если фильтр в CALCULATE относится к столбцу, который не отфильтрован в текущем контексте, фильтр в CALCULATE добавляется к существующему контексту.
○       Функции, такие как ALL(), ALLEXCEPT(), REMOVEFILTERS(), могут быть использованы внутри CALCULATE для удаления фильтров.

3.     Переоценка выражения: Выражение Expression переоценивается в новом, модифицированном контексте фильтра.

Примеры с AdventureWorks:
Магия CALCULATE
Давайте посмотрим, как CALCULATE преображает наши расчеты.

Пример 1: Продажи только для категории 'Bikes' (независимо от других фильтров)
Мы хотим меру, которая всегда показывает продажи велосипедов, даже если пользователь отфильтровал отчет по 'Clothing'.
Sales for Bikes =
CALCULATE (
   [Total Sales], -- Наша базовая мера Total Sales
   'Product'[Category] = "Bikes" -- Фильтр, который CALCULATE применит
)

●       Что происходит: CALCULATE берет меру [Total Sales]. Затем, прежде чем ее вычислить, он удаляет любой существующий фильтр по столбцу Product[Category] и применяет новый фильтр: Product[Category] = "Bikes".
●       Результат: Эта мера всегда будет показывать общие продажи велосипедов, независимо от того, что выбрано в срезах или других визуализациях по категории продукта.

Пример 2: Продажи за предыдущий год (Sales Last Year)
Это классический пример использования CALCULATE для временных сравнений.
Sales Last Year =
CALCULATE (
   [Total Sales],
   SAMEPERIODLASTYEAR ( 'Date'[Date] ) -- Функция времени, которая генерирует фильтр
)

●       Что происходит:
SAMEPERIODLASTYEAR('Date'[Date]) генерирует таблицу дат, которая соответствует тому же периоду, что и текущий контекст фильтра, но смещенному на один год назад. CALCULATE берет эту таблицу дат и заменяет ею текущий контекст фильтра по дате.

●       Результат: Если вы смотрите на продажи за июль 2007 года, Sales Last Year покажет продажи за июль 2006 года.

Пример 3: Процент от Общего Итога (Sales % of Grand Total)
Здесь нам нужно временно снять все фильтры, чтобы получить общий итог.
Sales % of Grand Total =
DIVIDE (
   [Total Sales], -- Продажи текущего контекста
   CALCULATE ( [Total Sales], ALL ( 'Product' ) ), -- Общие продажи, игнорируя фильтры по Product
   BLANK()
)

●       Что происходит:
CALCULATE ( [Total Sales], ALL ( 'Product' ) ) вычисляет [Total Sales], но при этом ALL('Product') удаляет все фильтры, которые были применены к таблице Product. Это позволяет получить общую сумму продаж по всем продуктам, независимо от того, какая категория или продукт выбраны в отчете.

●       Результат:
Если вы смотрите на продажи 'Bikes', эта мера покажет процент продаж 'Bikes' от общих продаж всех продуктов.
CALCULATE — это невероятно мощный инструмент, который позволяет вам буквально "перемещаться" по контексту фильтра, создавая сложные и динамичные аналитические сценарии.
Часть 5: Взаимодействие Контекстов: Когда они встречаются

Понимание того, как контекст строки и контекст фильтра взаимодействуют, является ключом к написанию продвинутых формул DAX.
Самый важный аспект здесь — это переход контекста строки в контекст фильтра (Row Context Transition).

Переход Контекста Строки в Контекст Фильтра
Когда вы вызываете меру (которая обычно работает в контексте фильтра) внутри функции-итератора (которая создает контекст строки), происходит магия: контекст строки временно преобразуется в контекст фильтра.

Это означает, что для каждой строки, по которой итерирует функция (например, SUMX), DAX создает временный фильтр, который включает все значения из этой текущей строки. Затем этот временный фильтр применяется к модели, и мера вычисляется в этом новом, специфическом для строки, контексте фильтра.

Пример с AdventureWorks:
Общая Прибыль для каждого заказа (с использованием меры прибыли)
Предположим, у нас есть мера [Total Profit] (сумма прибыли по всем продажам):
Total Profit = SUM(FactInternetSales[SalesAmount]) - SUM(FactInternetSales[TotalProductCost])

И мы хотим рассчитать общую прибыль для каждого заказа, а затем, возможно, просуммировать ее.
Total Profit Per Order Line =
SUMX (
   FactInternetSales, -- Итерируемпокаждойстроке FactInternetSales
   [Total Profit] -- Вызываеммеру Total Profit
)

●       Что происходит:
1.     SUMX начинает итерировать по FactInternetSales.
2.     Для первой строки FactInternetSales, SUMX создает контекст строки.
3.     Когда [Total Profit] вызывается внутри SUMX, происходит переход контекста: контекст строки (значения из текущей строки FactInternetSales) временно преобразуется в контекст фильтра.
4.     Меры SUM(FactInternetSales[SalesAmount]) и SUM(FactInternetSales[TotalProductCost]) внутри [Total Profit] теперь вычисляются в этом новом контексте фильтра, который фактически фильтрует данные до текущей строки. Таким образом, они возвращают SalesAmount и TotalProductCost именно для этой строки.
5.     Результат (Gross Profit Per Order) для этой строки возвращается SUMX.
6.     SUMX переходит к следующей строке и повторяет процесс.

Важно: Если бы вы просто написали FactInternetSales[SalesAmount] - FactInternetSales[TotalProductCost] внутри SUMX, это было бы вычисление в контексте строки. Но когда вы вызываете меру внутри итератора, происходит переход контекста.

Лучшие практики для работы с контекстом:
1.     Всегда думайте о контексте: Прежде чем писать формулу, задайте себе вопрос: "В каком контексте будет вычисляться эта формула? Это контекст строки или контекст фильтра? Как он будет меняться?"
2.     Используйте CALCULATE осознанно: Помните, что CALCULATE — это ваш основной инструмент для управления контекстом фильтра. Используйте его, когда вам нужно изменить поведение агрегации по умолчанию.
3.     Тестируйте формулы в разных контекстах: Создавайте простые таблицы или матрицы в Power BI, чтобы проверить, как ваши меры реагируют на различные фильтры и группировки. Это лучший способ понять, как работает контекст.
4.     Различайте вычисляемые столбцы и меры: Помните, что вычисляемые столбцы работают в контексте строки и статичны, а меры — в контексте фильтра и динамичны. Выбор правильного типа вычисления критически важен.
5.     Используйте переменные (VAR): Переменные не только улучшают читаемость, но и помогают вам контролировать контекст, так как выражение переменной вычисляется один раз в контексте, в котором она определена, а затем ее значение используется без повторного вычисления.
Заключение: Ваш Путь к Мастерству DAX Начинается Сейчас!

Поздравляю! Вы только что совершили самое важное погружение в мир DAX – вы освоили концепцию контекста вычисления. Вы поняли, что такое контекст строки, как он работает в вычисляемых столбцах и итераторах. Вы узнали, что контекст запроса — это высокоуровневый запрос клиента, который затем детализируется через контекст фильтра.
И, самое главное, вы раскрыли тайну контекста фильтра и его короля — функцию CALCULATE(), которая позволяет вам динамически управлять тем, как DAX смотрит на ваши данные.

Это не просто термины;
это фундаментальные принципы, которые позволят вам:
●       Создавать невероятно гибкие и мощные меры, которые адаптируются к любым вопросам пользователей.
●       Решать сложные аналитические задачи, такие как сравнения по времени, расчеты процентов и динамические ранги, которые были бы немыслимы в Excel или громоздки в SQL.
●       Превращать сырые данные AdventureWorks в действенные инсайты, которые помогут вашей компании принимать более обоснованные решения.

Ваш путь к мастерству DAX только начинается.
Самое главное сейчас — практика. Откройте Power BI Desktop (или Power Pivot в Excel), загрузите данные AdventureWorks и начните экспериментировать. Создавайте меры, играйте с ними в визуализациях, добавляйте срезы, смотрите, как меняется контекст. Делайте ошибки — это лучший способ учиться!

Я вложил в эту статью все свои силы, чтобы сделать ее максимально понятной и вдохновляющей. Надеюсь, вы почувствовали это. Теперь ваша очередь взять эти знания и превратить их в реальные аналитические суперспособности. Помните: понимание контекста — это не просто знание синтаксиса, это мышление на языке данных.
И вы уже на пути к тому, чтобы стать настоящим экспертом! Удачи!