Как выбрать запросом 1С из временной таблицы значения, которые встречаются только один раз?

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

При работе с запросами в платформе 1С:Предприятие разработчики часто сталкиваются с необходимостью фильтрации данных во временных таблицах. Одна из классических задач — оставить в выборке только те записи, которые присутствуют в таблице в единственном числе по определенному аналитическому разрезу (например, по контрагенту). При этом важно не просто получить список этих контрагентов, но и вернуть связанные с ними данные — даты, ссылки на документы-основания и другие реквизиты.

В этой статье мы подробно разберем, как правильно решить эту задачу на языке запросов 1С, проанализируем распространенную логическую ошибку, которую часто совершают программисты, и рассмотрим несколько эффективных альтернативных вариантов решения.

Анализ исходных данных

Представим, что у нас есть исходная временная таблица с именем ВремТаб, которая содержит следующие колонки: Контрагент, Дата, Ссылка. Нам необходимо отфильтровать ее таким образом, чтобы в результате остались только те строки, где контрагент встречается ровно один раз во всей таблице.

Рассмотрим пример исходных данных:

Ожидаемый результат выполнения запроса должен содержать только записи для «Васи» и «Сергея», так как «Вася1» и «Вася2» имеют дублирующие строки.

Разбор критической ошибки в популярном решении

На форумах часто предлагают решение, аналогичное представленному в Сообщении 5. Давайте проанализируем его структуру, чтобы понять, почему оно работает неверно.

В ошибочном варианте предлагается сгруппировать временную таблицу по двум полям сразу:


ГРУППИРОВАТЬ ПО
    Контрагент,
    Дата
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(Ссылка) = 1

Выясним причину, почему этот подход вернет неверные данные. Если один и тот же контрагент (например, «Вася2») встречается в исходной таблице дважды, но с разными датами (13.10 и 14.10), СУБД при группировке по паре полей Контрагент и Дата создаст две независимые группы:

  1. Группа 1: «Вася2» — 13.10 (Количество записей = 1)
  2. Группа 2: «Вася2» — 14.10 (Количество записей = 1)

Поскольку в каждой из этих групп количество записей действительно равно единице, условие секции ИМЕЮЩИЕ КОЛИЧЕСТВО(Ссылка) = 1 будет полностью выполнено для обеих строк. В результате «Вася2» ошибочно попадет в итоговый результат, нарушая главное требование задачи.

Правило: Чтобы определить уникальность записи по конкретному полю, группировку необходимо выполнять строго по этому полю, без добавления в секцию группировки сопутствующих реквизитов (таких как дата или ссылка).

Решение 1: Классический двухшаговый запрос с временной таблицей (Рекомендуемый)

Рассмотрим наиболее надежный и производительный способ решения задачи на платформе 1С. Мы разобьем задачу на два последовательных шага в рамках одного пакета запросов.

Шаг 1. Выделяем уникальных контрагентов, которые встречаются в исходной таблице ровно один раз, и помещаем их в промежуточную временную таблицу ВТ_УникальныеКонтрагенты.

Шаг 2. Соединяем исходную таблицу ВремТаб с полученной таблицей ВТ_УникальныеКонтрагенты по полю Контрагент. Благодаря внутреннему соединению в результат попадут только нужные строки со всеми исходными реквизитами (дата, ссылка и др.).

Посмотрим на пример реализации этого алгоритма:


// Шаг 1: Отбираем контрагентов, встречающихся ровно 1 раз
ВЫБРАТЬ
    ВремТаб.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_УникальныеКонтрагенты
ИЗ
    ВремТаб КАК ВремТаб

ГРУППИРОВАТЬ ПО
    ВремТаб.Контрагент

ИМЕЮЩИЕ
    КОЛИЧЕСТВО(ВремТаб.Контрагент) = 1

ИНДЕКСИРОВАТЬ ПО
    Контрагент
;

// Шаг 2: Получаем исходные данные по отобранным контрагентам
ВЫБРАТЬ
    ВремТаб.Контрагент КАК Контрагент,
    ВремТаб.Дата КАК Дата,
    ВремТаб.Ссылка КАК Ссылка
ИЗ
    ВремТаб КАК ВремТаб
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_УникальныеКонтрагенты КАК ВТ_УникальныеКонтрагенты
        ПО ВремТаб.Контрагент = ВТ_УникальныеКонтрагенты.Контрагент

Обратите внимание на важный нюанс: в первом запросе пакета мы добавили инструкцию ИНДЕКСИРОВАТЬ ПО Контрагент. Индексирование временной таблицы по полям связи существенно повышает скорость выполнения внутреннего соединения на больших объемах данных.

Решение 2: Использование вложенного запроса в одном пакете

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

Разберем по шагам этот вариант запроса:


ВЫБРАТЬ
    ВремТаб.Контрагент КАК Контрагент,
    ВремТаб.Дата КАК Дата,
    ВремТаб.Ссылка КАК Ссылка
ИЗ
    ВремТаб КАК ВремТаб
ГДЕ
    ВремТаб.Контрагент В
        (ВЫБРАТЬ
            ВТ_Группировка.Контрагент
        ИЗ
            ВремТаб КАК ВТ_Группировка
        ГРУППИРОВАТЬ ПО
            ВТ_Группировка.Contragent
        ИМЕЮЩИЕ
            КОЛИЧЕСТВО(ВТ_Группировка.Контрагент) = 1)

Этот способ более компактен в написании, однако на больших таблицах он может уступать в производительности первому варианту, так как СУБД сложнее оптимизировать выполнение вложенных подзапросов с агрегатными функциями.

Решение 3: Метод исключения дублей через ЛЕВОЕ СОЕДИНЕНИЕ

Иногда при работе с высоконагруженными базами данных СУБД быстрее обрабатывает операции исключения, чем прямую группировку с условием равенства. Мы можем применить альтернативную логику: сначала найти всех контрагентов-дублеров (которые встречаются более одного раза), а затем левым соединением исключить их из общего списка.

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


// Шаг 1: Собираем всех контрагентов, у которых есть дубли
ВЫБРАТЬ
    ВремТаб.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_ДублирующиеКонтрагенты
ИЗ
    ВремТаб КАК ВремТаб

ГРУППИРОВАТЬ ПО
    ВремТаб.Контрагент

ИМЕЮЩИЕ
    КОЛИЧЕСТВО(ВремТаб.Контрагент) > 1

ИНДЕКСИРОВАТЬ ПО
    Контрагент
;

// Шаг 2: Выбираем только те строки, которых нет в таблице дублей
ВЫБРАТЬ
    ВремТаб.Контрагент КАК Контрагент,
    ВремТаб.Дата КАК Дата,
    ВремТаб.Ссылка КАК Ссылка
ИЗ
    ВремТаб КАК ВремТаб
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДублирующиеКонтрагенты КАК ВТ_Дубли
        ПО ВремТаб.Контрагент = ВТ_Дубли.Контрагент
ГДЕ
    ВТ_Дубли.Контрагент ЕСТЬ NULL

В секции ГДЕ условие ВТ_Дубли.Контрагент ЕСТЬ NULL отсекает все строки, для которых сопоставление с таблицей дублей оказалось успешным. Таким образом, в результирующую выборку попадают только уникальные записи.

Важный нюанс: КОЛИЧЕСТВО() или КОЛИЧЕСТВО(РАЗЛИЧНЫЕ)?

При написании условий в секции ИМЕЮЩИЕ важно учитывать физический смысл и качество ваших данных во временной таблице ВремТаб:

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

← На главную