При работе с языком запросов 1С разработчики иногда сталкиваются с ситуацией, когда двух разных элементов справочника с одинаковыми наименованиями агрегатная функция КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) будто бы не различает. Возникает естественный вопрос: как именно платформа сравнивает значения ссылочных типов — по их визуальному представлению (наименованию) или по уникальному идентификатору в базе данных?
В этой статье мы подробно разберем механизм работы конструкций группировки и функции КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...), выясним причину ошибочных выводов и рассмотрим способы оптимизации запросов при подсчете уникальных значений.
Проанализируем базовый механизм платформы: в языке запросов 1С агрегатная функция КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) всегда выполняет сравнение по внутреннему типу данных и уникальному идентификатору (GUID). Если в табличной части или справочнике присутствуют две разные ссылки, которые ссылаются на разные элементы, платформа всегда посчитает их как два разных элемента, даже если у них полностью совпадают реквизиты Наименование, Код или любые другие поля.
Сравнение по строковому представлению происходит исключительно в том случае, если разработчик явно применил к полю функцию ПРЕДСТАВЛЕНИЕ() или выполнил приведение типа через ВЫРАЗИТЬ(... КАК СТРОКА).
Рассмотрим ситуацию, с которой столкнулся разработчик. В запросе подсчитывалось количество различных участков в табличной части договора, однако результат выдавал единицу для строк с разными элементами. Посмотрим на исходный текст запроса:
ВЫБРАТЬ
ДоговорыКонтрагентовСписокУчастков.Ссылка КАК Ссылка,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ДоговорыКонтрагентовСписокУчастков.Участок) КАК Участок
ИЗ
Справочник.ДоговорыКонтрагентов.СписокУчастков КАК ДоговорыКонтрагентовСписокУчастков
ГДЕ
ДоговорыКонтрагентовСписокУчастков.НомерСтроки > 1
СГРУППИРОВАТЬ ПО
ДоговорыКонтрагентовСписокУчастков.Ссылка
Выясним причину, почему запрос вернул неожиданное значение. Проблема заключалась не в алгоритме работы КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...), а в условии отбора ГДЕ НомерСтроки > 1. Отсекая первую строку табличной части, запрос физически исключал часть данных из выборки. Из-за этого количество попавших в выборку различных значений оказавалось меньше ожидаемого.
Разберем, как агрегатная функция ведет себя с особенными значениями, которые могут встретиться в базе данных:
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) полностью игнорирует `NULL`. Если в проверяемой колонке находится `NULL`, он не попадает в итоговый подсчет.Справочник.Номенклатура.ПустаяСсылка()) является полноценным значением. Если в выборке встречается несколько пустых ссылок одного типа, платформа посчитает их как один уникальный элемент.<Объект не найден...>, функция посчитает каждую такую ссылку как отдельное уникальное значение, так как их GUID различаются.При использовании агрегатной функции КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) следует учитывать важные платформенные ограничения:
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле1, Поле2) платформой не поддерживаются.РАЗЛИЧНЫЕ нельзя использовать для полей неограниченной длины (строки неограниченной длины, ХранилищеЗначения).На стороне системы управления базами данных (СУБД) данная функция транслируется в конструкцию COUNT(DISTINCT ...). На больших объемах данных в табличных частях это может приводить к повышенной нагрузке. Для оптимизации производительности рекомендуем использовать двухшаговый подход с применением временных таблиц.
Разберем по шагам создание такой оптимизированной конструкции:
// Шаг 1: Выбираем уникальные комбинации полей во временную таблицу
ВЫБРАТЬ РАЗЛИЧНЫЕ
ТЧ.Ссылка КАК Ссылка,
ТЧ.Участок КАК Участок
ПОМЕСТИТЬ ВТ_УникальныеУчастки
ИЗ
Справочник.ДоговорыКонтрагентов.СписокУчастков КАК ТЧ;
// Шаг 2: Подсчитываем количество записей обычным КОЛИЧЕСТВО(*)
ВЫБРАТЬ
ВТ.Ссылка КАК Ссылка,
КОЛИЧЕСТВО(*) КАК УчастокКоличество
ИЗ
ВТ_УникальныеУчастки КАК ВТ
СГРУППИРОВАТЬ ПО
ВТ.Ссылка;
Такой подход с предварительным отбором через ПОМЕСТИТЬ позволяет СУБД построить более эффективный план выполнения и существенно ускорить работу запроса на больших массивах данных.