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

DISTINCT и VALUES: в чём разница

Обе возвращают уникальные значения, но по-разному ведут себя с «осиротевшими» строками. Из-за этого итог может молча потерять данные.

DISTINCT и VALUES выглядят как синонимы: обе возвращают уникальные значения столбца в текущем контексте. На чистых данных они и правда дают одно и то же. Но есть один сценарий, где они расходятся, — и из-за него итоговая мера может молча потерять часть выручки. Разбираем по статье SQLBI.

Что возвращают обе

VALUES ( Регионы[Регион] )     -- уникальные регионы в контексте
DISTINCT ( Регионы[Регион] )   -- то же самое... почти

Обе принимают столбец (вернут одну колонку уникальных значений) или таблицу (вернут уникальные строки). В 99% случаев на корректной модели результат идентичен. Разница — в одной строке.

Ключевое различие: пустая строка целостности

Представим нарушение целостности (referential integrity violation): в Продажи есть КодРегиона, которого нет в справочнике Регионы. Такое бывает: в факт попал регион, забытый в справочнике. Движок в этом случае добавляет в измерение пустую строку — «корзину» для всех осиротевших продаж.

  • VALUES ( Регионы[Регион] )включает эту пустую строку;
  • DISTINCT ( Регионы[Регион] )исключает её.
Суть в одной фразе

VALUES видит «сиротские» строки факта (через пустую строку целостности), DISTINCT — нет. На чистой модели разницы нет; на грязной — есть, и она тихая.

Почему это ломает итог

Когда вы итерируете (SUMX, AVERAGEX) по уникальным значениям, выбор функции решает судьбу осиротевших продаж.

Выручка со скидкой (НЕВЕРНО) =
SUMX (
    DISTINCT ( Регионы[Регион] ),
    [Выручка] * IF ( Регионы[Регион] = "ЮФО", 0.95, 1 )
)

Здесь DISTINCT выкинул пустую строку — и продажи без региона выпали из суммы. Итог занижен, и это не бросается в глаза: цифра правдоподобная, просто неправильная.

Выручка со скидкой (ВЕРНО) =
SUMX (
    VALUES ( Регионы[Регион] ),
    [Выручка] * IF ( Регионы[Регион] = "ЮФО", 0.95, 1 )
)

VALUES прогоняет и пустую строку — осиротевшие продажи учтены, итог сходится с общей выручкой.

Тихий баг

Самое коварное: на тестовых (чистых) данных DISTINCT и VALUES дают одинаковый результат, и ошибку не видно. Она «выстреливает» в проде, когда в факт попадает ключ, которого нет в справочнике. Поэтому для итераторов-сумматоров берите VALUES по умолчанию.

Когда, наоборот, нужен DISTINCT

Для статистики — среднее, минимум, максимум, перцентили — пустая строка «Unknown» искажает результат: все осиротевшие записи схлопываются в одну группу и тянут среднее.

Средняя выручка по товару =
AVERAGEX ( DISTINCT ( Товары[Товар] ), [Выручка] )

Тут DISTINCT уместен: мы усредняем по реальным товарам, и фиктивная «пустая» группа только испортила бы среднее.

Правило выбора

ЗадачаБрать
Итератор-сумматор (SUMX, SUMMARIZE для итога)VALUES — не теряет осиротевшие строки
Статистика (AVERAGEX, MINX, MAXX, перцентили)DISTINCT — без фиктивной группы
Текущий выбор пользователяVALUES (на ней основан SELECTEDVALUE)
Лучше — чинить данные

Расхождение DISTINCT/VALUES — симптом нарушенной целостности. Правильный путь: не выбирать функцию под баг, а починить источник — добить справочник недостающими ключами в ETL. Тогда обе функции дадут одно и то же, а пустая строка исчезнет.

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

Сравните DISTINCTCOUNT ( Продажи[КодТовара] ) и COUNTROWS ( VALUES ( Товары[КодТовара] ) ) без срезов. Совпадут ли — и чему равны на наборе курса?

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

Обе = 25. В каталоге 25 товаров, и все встречаются в продажах, поэтому числа сходятся. Разойтись они могли бы при «осиротевших» ключах (товар в продажах есть, в справочнике нет) — тогда VALUES добавила бы пустую строку целостности. На чистом наборе курса этого нет.

Что дальше

VALUES по умолчанию для сумм, DISTINCT для статистики, а в идеале — чистые ключи, при которых вопрос снимается. Дальше в курсе — field parameters, what-if и другие приёмы. Курс пополняется.

Почему `SUMX ( DISTINCT ( Регионы[Регион] ), … )` может занизить итог?
При нарушении целостности движок добавляет в измерение пустую строку для осиротевших продаж. VALUES её включает, DISTINCT — нет. Поэтому для сумматоров-итераторов берут VALUES, а корень проблемы лечат чисткой ключей.
Прогресс сохраняется в вашем браузере.
§ Power BI под ключ

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

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

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