Как убрать дублирование контактных лиц в запросе 1С при фильтрации по нескольким функциям

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с запросами в 1С начинающие разработчики часто сталкиваются с ситуацией, когда результат выборки содержит повторяющиеся строки. Типичный пример — получение списка контактных лиц, у которых может быть несколько ролей или функций. Если мы пытаемся отфильтровать список по нескольким кодам функций одновременно, система выдает отдельную строку для каждого совпадения, что приводит к «размножению» одного и того же человека в отчете. В этой статье мы подробно разберем, почему это происходит и как правильно составить запрос, чтобы получить уникальный список записей.

Почему появляются дубли в запросах с табличными частями

Проанализируем механику работы платформы 1С. Когда мы используем ЛЕВОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ между основной таблицей справочника (например, Справочник.КартаПредприятия) и его табличной частью (КартаПредприятия.Функции), система создает так называемое декартово произведение. Если у одного контактного лица в табличной части указано три разные функции, и все они попадают под условия фильтра, запрос честно вернет три строки. Для СУБД эти строки различны, так как значения в поле «Функция» отличаются.

Рассмотрим ситуацию на примере кода автора: у Ивановой есть функции с кодами «000000020» и «000000018». При поиске по обоим кодам запрос выведет Иванову дважды. Чтобы этого избежать, нам нужно заставить систему «схлопнуть» эти записи в одну.

Способ 1: Использование агрегатных функций и группировки

Это самый надежный и распространенный метод. Суть его заключается в том, чтобы сгруппировать результат по всем полям, которые должны быть уникальными, а для «виновника» дублей (поля с функцией) применить агрегатную функцию МАКСИМУМ или МИНИМУМ. Таким образом, даже если у человека пять функций, будет выбрана только одна (самая большая или самая маленькая по алфавиту/коду).

Разберем обновленный текст запроса с применением группировки:


ВЫБРАТЬ
    КартаПредприятия.Владелец КАК Владелец,
    КартаПредприятия.Ссылка КАК КонтактноеЛицо,
    КартаПредприятия.Наименование КАК ФИО,
    КартаПредприятия.Должность КАК Должность,
    МАКСИМУМ(КартаПредприятияФункции.Функция.Код) КАК КодФункции
ИЗ
    Справочник.КартаПредприятия КАК КартаПредприятия
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.КартаПредприятия.Функции КАК КартаПредприятияФункции
        ПО КартаПредприятия.Ссылка = КартаПредприятияФункции.Ссылка
ГДЕ
    КартаПредприятия.Владелец = &Владелец
    И КартаПредприятияФункции.Функция.Код В (&СписокКодов)
    И НЕ КартаПредприятия.Состояние В (&НеАктивные)

СГРУППИРОВАТЬ ПО
    КартаПредприятия.Владелец,
    КартаПредприятия.Ссылка,
    КартаПредприятия.Наименование,
    КартаПредприятия.Должность

Важный нюанс: при использовании СГРУППИРОВАТЬ ПО все поля, перечисленные в секции ВЫБРАТЬ, которые не обернуты в агрегатные функции (СУММА, МАКСИМУМ, КОЛИЧЕСТВО), обязательно должны быть указаны в блоке группировки. В данном примере мы группируем по ссылке и ФИО, а код функции выбираем максимальный из имеющихся.

Способ 2: Условие в подзапросе (В ИЗ ВЫБРАТЬ)

Если нам вообще не нужно выводить наименование или код функции в итоговую таблицу, а функция требуется только для фильтрации, мы можем исключить соединение с табличной частью из основного запроса. Вместо этого используем конструкцию ГДЕ Ссылка В (...).

Проанализируем, как это упрощает структуру:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    КартаПредприятия.Владелец,
    КартаПредприятия.Наименование КАК КонтактноеЛицо,
    КартаПредприятия.Должность
ИЗ
    Справочник.КартаПредприятия КАК КартаПредприятия
ГДЕ
    КартаПредприятия.Владелец = &Владелец
    И КартаПредприятия.Ссылка В (
        ВЫБРАТЬ
            ТЧ_Функции.Ссылка
        ИЗ
            Справочник.КартаПредприятия.Функции КАК ТЧ_Функции
        ГДЕ
            ТЧ_Функции.Функция.Код В (&СписокКодов)
    )
    И НЕ КартаПредприятия.Состояние В (&НеАктивные)

Этот метод автоматически исключает дублирование, так как основная таблица КартаПредприятия выбирается без соединений, а подзапрос лишь проверяет наличие хотя бы одной подходящей функции для каждой ссылки. Это работает быстрее и чище с точки зрения структуры данных.

Оптимизация запроса: советы для начинающих

Посмотрим на советы опытных разработчиков, которые прозвучали в обсуждении. При написании запросов в 1С хорошим тоном считается соблюдение следующих правил:

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

Приоритезация функций с помощью конструкции ВЫБОР

Иногда просто взять МАКСИМУМ недостаточно. Предположим, если у контакта есть функция «Директор», нам важно вывести именно её, даже если есть другие коды. В таком случае в запросе можно создать «вес» для каждой функции.

Разберем пример реализации такой логики:


ВЫБРАТЬ
    КартаПредприятия.Ссылка КАК КонтактноеЛицо,
    МИНИМУМ(ВЫБОР
        КОГДА КартаПредприятияФункции.Функция.Код = "000000020" ТОГДА 1
        КОГДА КартаПредприятияФункции.Функция.Код = "000000017" ТОГДА 2
        ИНАЧЕ 3
    КОНЕЦ) КАК ВесФункции
ПОМЕСТИТЬ ВТ_Приоритеты
ИЗ
    Справочник.КартаПредприятия.Функции КАК КартаПредприятияФункции
ГДЕ
    КартаПредприятияФункции.Функция.Код В (&СписокКодов)
СГРУППИРОВАТЬ ПО
    КартаПредприятия.Ссылка
;

ВЫБРАТЬ
    КартаПредприятия.Наименование,
    ВТ_Приоритеты.ВесФункции
ИЗ
    ВТ_Приоритеты КАК ВТ_Приоритеты
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.КартаПредприятия КАК КартаПредприятия
        ПО ВТ_Приоритеты.КонтактноеЛицо = КартаПредприятия.Ссылка

Используя такую методику, мы сначала вычисляем наиболее важную функцию для каждого человека во временной таблице, а затем получаем данные самого справочника. Это гарантирует отсутствие дублей и предсказуемость результата.

Подведем итог: Для решения проблемы дублей в 1С используйте группировку (СГРУППИРОВАТЬ ПО) в сочетании с агрегатными функциями или фильтрацию через подзапросы с оператором В. Это сделает ваши отчеты корректными, а код профессиональным.

← На главную