ALL мы используем как «убрать фильтры» — например, в знаменателе доли. Но есть сценарий, где ALL убирает не всё, и доля молча ломается. Лечит это редкая, но важная функция ALLCROSSFILTERED. Разбираем по статье SQLBI.
Базовый разбор — на сквозном наборе (Продажи, Регионы). Для демонстрации поломки ниже используется сценарий из статьи SQLBI: вспомогательная таблица «интересные бренды» с ограниченной связью и мера маркетингового бюджета [Бюджет] — это гипотетический пример, а не часть учебного набора.
Что убирает ALL
ALL ( Таблица ) снимает фильтры с самой таблицы и её расширенной таблицы (expanded table) — то есть с измерений, связанных обычными связями «один ко многим». В классической «звезде» этого достаточно: ALL(Продажи) уберёт и фильтры, пришедшие из Товары, Регионы, Календарь.
Доля региона =
DIVIDE ( [Выручка], CALCULATE ( [Выручка], ALL ( Регионы ) ) )
Пока связи обычные и однонаправленные — работает.
Где ALL не справляется
ALL опирается на механизм расширенной таблицы, а он есть не у всех связей. Фильтр может приходить в таблицу не через обычную связь, а через cross-filter — и тогда ALL его не видит. Так бывает при:
- двунаправленных связях (фильтр идёт «обратно»);
- ограниченных связях (limited relationships): «многие ко многим» по кардинальности, разные «острова данных», композитные модели.
В этих случаях ALL(Продажи) снимет «прямые» фильтры, но оставит те, что пришли через перекрёстную фильтрацию. Доля считается от неполного знаменателя — и, например, всегда выходит 100%.
По меткому замечанию SQLBI, функцию правильнее было бы назвать ALLCROSSFILTERING: она убирает фильтр не с тех столбцов, что фильтрует таблица, а с тех, что фильтруют саму таблицу через перекрёстную фильтрацию. То есть снимает все источники cross-filter, до которых ALL не дотягивается.
Пример, где ALL даёт неверно
Сценарий из статьи: таблица «интересные бренды» создана из товаров и связана ограниченной связью; есть перекрёстная фильтрация. Считаем долю маркетингового бюджета.
% бюджета (НЕВЕРНО) =
DIVIDE ( [Бюджет], CALCULATE ( [Бюджет], ALL ( Продажи ) ) )
-- всегда 100%: ALL не снял cross-filter от связанной таблицы
% бюджета (ВЕРНО) =
DIVIDE ( [Бюджет], CALCULATE ( [Бюджет], ALLCROSSFILTERED ( Продажи ) ) )
-- ALLCROSSFILTERED убирает и перекрёстную фильтрацию
ALLCROSSFILTERED снял фильтры, пришедшие через ограниченную/двунаправленную связь, — знаменатель стал полным, доля посчиталась правильно.
Ограничения функции
- Только модификатор CALCULATE.
ALLCROSSFILTEREDнельзя использовать как самостоятельное табличное выражение (в отличие отALL, которое возвращает таблицу). Только внутриCALCULATE/CALCULATETABLEкак фильтр-модификатор. - Один аргумент — имя таблицы.
Когда это вспоминать
ALLCROSSFILTERED нужен редко — но именно в тех моделях, где он нужен, его отсутствие даёт тихо неверные доли. Подозревайте его, если:
- в модели есть двунаправленные связи или «многие ко многим» (привет уроку про таблицу-мост);
- это композитная модель или несколько островов данных;
- доля/процент «всегда 100%» или знаменатель явно неполный, хотя
ALLстоит.
Большинству моделей ALLCROSSFILTERED не нужен — потому что в них нет лишней двунаправленности. Это ещё один довод за однонаправленные связи по умолчанию: меньше ситуаций, где ALL обманывает. Включайте двунаправленность точечно — и помните про ALLCROSSFILTERED, если включили.
Что дальше
Тонкости фильтр-контекста на сложных связях разобрали. Дальше в курсе — field parameters, what-if и DAX-паттерны. Курс пополняется.