Урок 10 · 8 мин чтения

ALLSELECTED: где безопасно, а где ловушка

Удобная функция для «доли от видимого» с коварным поведением внутри итераторов. Разбираем безопасное применение и правило, которое спасает от тихих багов.

ALLSELECTED мы уже встречали: «доля в выборке» считается через неё. Функция удобная и интуитивная — ровно до момента, когда её затягивает внутрь итератора. Тогда она начинает вести себя непредсказуемо, и баг почти не отлаживается. Разбираем по статье SQLBI о best practices.

Что она делает

ALLSELECTED восстанавливает контекст фильтра «как его выбрал пользователь» — без фильтра текущей строки визуала, но с учётом срезов. Отсюда её каноничное применение — доля от итога видимого:

Доля категории =
DIVIDE (
    [Выручка],
    CALCULATE ( [Выручка], ALLSELECTED ( Товары[Категория] ) )
)

В строке «Молочка» числитель — выручка молочки, знаменатель — выручка по всем видимым категориям. Если пользователь срезом оставил три категории, доли посчитаются внутри этих трёх и дадут в сумме 100%. Это и есть безопасный сценарий.

Почему она коварна: shadow filter context

Под капотом ALLSELECTED работает не как простое «убрать фильтр визуала». Она опирается на теневой контекст фильтра (shadow filter context) — скрытый контекст, который создают итераторы. В разных ситуациях механизм разный:

  • в SUMMARIZECOLUMNS (обычный визуал) — убирает фильтр текущей группы, восстанавливая выбор;
  • внутри итератора (SUMX, ADDCOLUMNS) — активирует теневой контекст итерации.

Из-за этого ALLSELECTED в простом визуале и ALLSELECTED внутри итератора — по сути разные вещи, хотя пишутся одинаково.

Главная ловушка

Если мера с ALLSELECTED вызвана изнутри итератора, включается механизм теневого контекста — и результат становится нелогичным. В разборе SQLBI значение строки оказывалось больше итога — логически невозможное, верный признак, что контекст «поехал».

Пример бага

Допустим, хотим «заметные товары» — те, что дают ≥0,5% выручки. Если порог считать через меру с ALLSELECTED внутри SUMX:

Заметные товары (НЕВЕРНО) =
SUMX (
    Товары,
    IF ( DIVIDE ( [Выручка], [Выручка по выбору] ) >= 0.005, [Выручка] )
)
-- [Выручка по выбору] содержит ALLSELECTED → вызов из итератора → теневой контекст → мусор

[Выручка по выбору] с ALLSELECTED, вызванная из SUMX, ведёт себя не так, как в визуале. Цифры неверные, и понять почему — почти невозможно, особенно если ALLSELECTED спрятан в цепочке мер.

Как правильно

Правило одно: ALLSELECTED не должен сработать в момент начала итерации. Вычислите его значение до итератора и положите в переменную:

Заметные товары (ВЕРНО) =
VAR _Total = CALCULATE ( [Выручка], ALLSELECTED ( Товары ) )
RETURN
    SUMX (
        Товары,
        IF ( DIVIDE ( [Выручка], _Total ) >= 0.005, [Выручка] )
    )

ALLSELECTED отработал один раз снаружи, в нормальном контексте; в SUMX идёт уже готовое число _Total. Теневой контекст не включается — результат корректный.

Три правила ALLSELECTED
  1. Используйте ALLSELECTED только в мерах, которые кладёте прямо в визуал (доля от видимого). 2. Не вызывайте меры с ALLSELECTED из других мер и из итераторов. 3. Если значение нужно в итераторе — вынесите его в VAR до SUMX.

ALLSELECTED или REMOVEFILTERS

Из-за коварства ALLSELECTED его область применения стоит держать узкой:

  • нужна доля от выбранного пользователем (итог визуала) → ALLSELECTED, только в верхнеуровневой мере;
  • нужен итог по всем данным независимо от среза → REMOVEFILTERS / ALL — они проще и предсказуемее;
  • сложная логика с порогами и ранжированием внутри итераторов → считайте базу через ALL/REMOVEFILTERS или выносите ALLSELECTED в VAR.
Минимизируйте, а не запрещайте

ALLSELECTED не «плохая» — она незаменима для процента от видимого. Опасна не функция, а её использование в глубине цепочек мер и итераторов. Держите её на поверхности — и проблем не будет.

Проверь себя на наборе

Сделайте Доля = DIVIDE ( [Выручка], CALCULATE ( [Выручка], ALL ( Регионы ) ) ) и положите по округам. Чему равна сумма столбца «Доля»? А если заменить ALL на ALLSELECTED и оставить в срезе 3 округа?

Показать ответ

С ALL знаменатель — всегда все 8 округов; сумма видимых долей = 100 % (доля ЦФО ≈ 25,7 %). С ALLSELECTED и срезом из 3 округов знаменатель = сумма этих трёх, доли пересчитываются «от выбранного» — их сумма снова 100 %, но уже среди выбранных. В этом и разница: ALL игнорирует срез, ALLSELECTED его уважает.

Что дальше

С тонкостями фильтр-контекста (ALL, ALLSELECTED, ALLCROSSFILTERED) разобрались. Дальше — field parameters, what-if и DAX-паттерны. Курс пополняется.

Где `ALLSELECTED` ведёт себя коварно и даёт нелогичные результаты?
Внутри итератора активируется механизм теневого контекста, и ALLSELECTED работает иначе, чем в визуале (строка может превысить итог). Правило: вычислять ALLSELECTED в VAR до итерации, а саму функцию держать в верхнеуровневых мерах.
Прогресс сохраняется в вашем браузере.
§ Power BI под ключ

Нужно внедрить
это в компании?

Соберём DWH, модель и дашборды под ваши данные. Бесплатная консультация — 30 минут.

Телефон+7 918 042 34 43