При работе с запросами в 1С начинающие разработчики часто сталкиваются с ситуацией, когда результат выборки содержит повторяющиеся строки. Типичный пример — получение списка контактных лиц, у которых может быть несколько ролей или функций. Если мы пытаемся отфильтровать список по нескольким кодам функций одновременно, система выдает отдельную строку для каждого совпадения, что приводит к «размножению» одного и того же человека в отчете. В этой статье мы подробно разберем, почему это происходит и как правильно составить запрос, чтобы получить уникальный список записей.
Проанализируем механику работы платформы 1С. Когда мы используем ЛЕВОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ между основной таблицей справочника (например, Справочник.КартаПредприятия) и его табличной частью (КартаПредприятия.Функции), система создает так называемое декартово произведение. Если у одного контактного лица в табличной части указано три разные функции, и все они попадают под условия фильтра, запрос честно вернет три строки. Для СУБД эти строки различны, так как значения в поле «Функция» отличаются.
Рассмотрим ситуацию на примере кода автора: у Ивановой есть функции с кодами «000000020» и «000000018». При поиске по обоим кодам запрос выведет Иванову дважды. Чтобы этого избежать, нам нужно заставить систему «схлопнуть» эти записи в одну.
Это самый надежный и распространенный метод. Суть его заключается в том, чтобы сгруппировать результат по всем полям, которые должны быть уникальными, а для «виновника» дублей (поля с функцией) применить агрегатную функцию МАКСИМУМ или МИНИМУМ. Таким образом, даже если у человека пять функций, будет выбрана только одна (самая большая или самая маленькая по алфавиту/коду).
Разберем обновленный текст запроса с применением группировки:
ВЫБРАТЬ
КартаПредприятия.Владелец КАК Владелец,
КартаПредприятия.Ссылка КАК КонтактноеЛицо,
КартаПредприятия.Наименование КАК ФИО,
КартаПредприятия.Должность КАК Должность,
МАКСИМУМ(КартаПредприятияФункции.Функция.Код) КАК КодФункции
ИЗ
Справочник.КартаПредприятия КАК КартаПредприятия
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.КартаПредприятия.Функции КАК КартаПредприятияФункции
ПО КартаПредприятия.Ссылка = КартаПредприятияФункции.Ссылка
ГДЕ
КартаПредприятия.Владелец = &Владелец
И КартаПредприятияФункции.Функция.Код В (&СписокКодов)
И НЕ КартаПредприятия.Состояние В (&НеАктивные)
СГРУППИРОВАТЬ ПО
КартаПредприятия.Владелец,
КартаПредприятия.Ссылка,
КартаПредприятия.Наименование,
КартаПредприятия.Должность
Важный нюанс: при использовании СГРУППИРОВАТЬ ПО все поля, перечисленные в секции ВЫБРАТЬ, которые не обернуты в агрегатные функции (СУММА, МАКСИМУМ, КОЛИЧЕСТВО), обязательно должны быть указаны в блоке группировки. В данном примере мы группируем по ссылке и ФИО, а код функции выбираем максимальный из имеющихся.
Если нам вообще не нужно выводить наименование или код функции в итоговую таблицу, а функция требуется только для фильтрации, мы можем исключить соединение с табличной частью из основного запроса. Вместо этого используем конструкцию ГДЕ Ссылка В (...).
Проанализируем, как это упрощает структуру:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
КартаПредприятия.Владелец,
КартаПредприятия.Наименование КАК КонтактноеЛицо,
КартаПредприятия.Должность
ИЗ
Справочник.КартаПредприятия КАК КартаПредприятия
ГДЕ
КартаПредприятия.Владелец = &Владелец
И КартаПредприятия.Ссылка В (
ВЫБРАТЬ
ТЧ_Функции.Ссылка
ИЗ
Справочник.КартаПредприятия.Функции КАК ТЧ_Функции
ГДЕ
ТЧ_Функции.Функция.Код В (&СписокКодов)
)
И НЕ КартаПредприятия.Состояние В (&НеАктивные)
Этот метод автоматически исключает дублирование, так как основная таблица КартаПредприятия выбирается без соединений, а подзапрос лишь проверяет наличие хотя бы одной подходящей функции для каждой ссылки. Это работает быстрее и чище с точки зрения структуры данных.
Посмотрим на советы опытных разработчиков, которые прозвучали в обсуждении. При написании запросов в 1С хорошим тоном считается соблюдение следующих правил:
ВЫБРАТЬ. Это предотвратит ошибки при выполнении запроса под пользователем с ограниченными правами доступа (RLS).Код = "001" ИЛИ Код = "002", лучше использовать оператор В (&СписокКодов) и передавать в параметр массив значений. Это значительно упрощает чтение кода и помогает СУБД строить более эффективный план выполнения запроса.КартаПредприятияФункции.Функция.Код заставляют систему делать неявные соединения с другими таблицами. Если данных много, это может замедлить работу.Иногда просто взять МАКСИМУМ недостаточно. Предположим, если у контакта есть функция «Директор», нам важно вывести именно её, даже если есть другие коды. В таком случае в запросе можно создать «вес» для каждой функции.
Разберем пример реализации такой логики:
ВЫБРАТЬ
КартаПредприятия.Ссылка КАК КонтактноеЛицо,
МИНИМУМ(ВЫБОР
КОГДА КартаПредприятияФункции.Функция.Код = "000000020" ТОГДА 1
КОГДА КартаПредприятияФункции.Функция.Код = "000000017" ТОГДА 2
ИНАЧЕ 3
КОНЕЦ) КАК ВесФункции
ПОМЕСТИТЬ ВТ_Приоритеты
ИЗ
Справочник.КартаПредприятия.Функции КАК КартаПредприятияФункции
ГДЕ
КартаПредприятияФункции.Функция.Код В (&СписокКодов)
СГРУППИРОВАТЬ ПО
КартаПредприятия.Ссылка
;
ВЫБРАТЬ
КартаПредприятия.Наименование,
ВТ_Приоритеты.ВесФункции
ИЗ
ВТ_Приоритеты КАК ВТ_Приоритеты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.КартаПредприятия КАК КартаПредприятия
ПО ВТ_Приоритеты.КонтактноеЛицо = КартаПредприятия.Ссылка
Используя такую методику, мы сначала вычисляем наиболее важную функцию для каждого человека во временной таблице, а затем получаем данные самого справочника. Это гарантирует отсутствие дублей и предсказуемость результата.
Подведем итог: Для решения проблемы дублей в 1С используйте группировку (СГРУППИРОВАТЬ ПО) в сочетании с агрегатными функциями или фильтрацию через подзапросы с оператором В. Это сделает ваши отчеты корректными, а код профессиональным.