Как в запросе 1С найти объект с точным совпадением состава строк множеству?

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

В практике разработки на платформе 1С часто возникает задача поиска объектов (документов комплектации, спецификаций номенклатуры, наборов), табличная часть которых строго совпадает с заданным набором элементов. Например, если в базе есть комплект А с составом (Т1, Т2), комплект Б с составом (Т1, Т2, Т3) и комплект С с составом (Т1, Т3), при поиске по эталону (Т1, Т2) в выборку должен попасть исключительно комплект А. Комплект Б включать нельзя, так как в нем присутствует лишняя позиция Т3.

В теории реляционных баз данных такая операция называется точным реляционным делением (Exact Relational Division). Для ее выполнения необходимо одновременно проверить два условия: все элементы эталона входят в состав проверяемого объекта, и общее количество уникальных позиций объекта строго равно количеству позиций в эталоне. Разберем основные способы реализации этой логики на языке запросов 1С.

Способ 1. Двусторонний подсчет количества через группировку и левое соединение

Рассмотрим наиболее компактный и производительный вариант. Передадим эталонный список позиций во временную таблицу и соединим ее с табличной частью проверяемых объектов с помощью левого соединения. Далее сгруппируем данные по объекту и проверим совпадение мощностей множеств в секции ИМЕЮЩИЕ.

Разберем по шагам логику этого запроса:

  1. Помещаем эталонный состав во временную таблицу ВТОбразец и вычисляем количество элементов в эталоне (параметр &КоличествоОбразец).
  2. Выполняем ЛЕВОЕ СОЕДИНЕНИЕ таблицы комплектующих с образцом по полю Номенклатура.
  3. В условии ИМЕЮЩИЕ проверяем: количество совпавших позиций с эталоном должно быть равно общему количеству строк в комплектации, а также равно числу элементов в переданном образце.

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


ВЫБРАТЬ
    Комплектация.Ссылка КАК Ссылка
ИЗ
    Документ.КомплектацияНоменклатуры.Комплектующие КАК Комплектация
        ЛЕВОЕ СОЕДИНЕНИЕ ВТОбразец КАК Образец
        ПО (Образец.Номенклатура = Комплектация.Номенклатура)
ГДЕ
    Комплектация.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
    Комплектация.Ссылка
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Комплектация.Номенклатура) = КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Образец.Номенклатура)
    И КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Образец.Номенклатура) = &КоличествоОбразец

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

Способ 2. Поэтапная фильтрация через временные таблицы и исключение несовпадений

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

Разберем этапы реализации:

  1. Подсчитываем количество элементов в эталонном отборе и сохраняем его во временную таблицу.
  2. Группируем исходные спецификации по ссылкам и считаем число позиций номенклатуры в каждой из них.
  3. Отбираем спецификации, у которых общее количество строк совпадает с размером эталона.
  4. Через левое соединение с эталоном и проверку ЕСТЬ NULL находим те спецификации, в которых присутствуют номенклатурные позиции, отсутствующие в отборе (выявляем лишние строки).
  5. Исключаем из первого набора спецификации, содержащие лишние строки.

Посмотрим на практический пример запроса для спецификаций:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_Отбор
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Ссылка В(&МассивНоменклатуры)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_Отбор.Номенклатура) КАК Количество
ПОМЕСТИТЬ ВТ_КоличествоЭталон
ИЗ
    ВТ_Отбор КАК ВТ_Отбор
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Спецификация.Ссылка КАК Ссылка,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Спецификация.Номенклатура) КАК Количество
ПОМЕСТИТЬ ВТ_СпецификацииВсе
ИЗ
    Справочник.СпецификацииНоменклатуры.ИсходныеКомплектующие КАК Спецификация
СГРУППИРОВАТЬ ПО
    Спецификация.Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_СпецификацииВсе.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_КандидатыПоКоличеству
ИЗ
    ВТ_СпецификацииВсе КАК ВТ_СпецификацииВсе,
    ВТ_КоличествоЭталон КАК ВТ_КоличествоЭталон
ГДЕ
    ВТ_СпецификацииВсе.Количество = ВТ_КоличествоЭталон.Количество
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    Спецификация.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ИсключаемыеСпецификации
ИЗ
    Справочник.СпецификацииНоменклатуры.ИсходныеКомплектующие КАК Спецификация
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Отбор КАК ВТ_Отбор
        ПО (ВТ_Отбор.Номенклатура = Спецификация.Номенклатура)
ГДЕ
    Спецификация.Ссылка В
        (ВЫБРАТЬ
            ВТ_КандидатыПоКоличеству.Ссылка
        ИЗ
            ВТ_КандидатыПоКоличеству)
    И ВТ_Отбор.Номенклатура ЕСТЬ NULL
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_КандидатыПоКоличеству.Ссылка КАК Ссылка
ИЗ
    ВТ_КандидатыПоКоличеству КАК ВТ_КандидатыПоКоличеству
ГДЕ
    НЕ ВТ_КандидатыПоКоличеству.Ссылка В
        (ВЫБРАТЬ
            ВТ_ИсключаемыеСпецификации.Ссылка
        ИЗ
            ВТ_ИсключаемыеСпецификации)

Способ 3. Отсечение лишних записей с последующей проверкой мощности

Рассмотрим еще один лаконичный вариант, когда на первом шаге отсекаются все комплектации, содержащие хотя бы один элемент не из списка, а на втором шаге проверяется совпадение количества элементов.

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


ВЫБРАТЬ
    К1.Ссылка КАК Ссылка,
    К1.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ПодходящиеКомплектации
ИЗ
    Документ.КомплектацияНоменклатуры.Комплектующие КАК К1
        ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
            К0.Ссылка КАК Ссылка
        ИЗ
            Документ.КомплектацияНоменклатуры.Комплектующие КАК К0
        ГДЕ
            НЕ К0.Номенклатура В (&НаборНоменклатуры)) КАК К2
        ПО К1.Ссылка = К2.Ссылка
ГДЕ
    К2.Ссылка ЕСТЬ NULL
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ПодходящиеКомплектации.Ссылка КАК Ссылка
ИЗ
    ВТ_ПодходящиеКомплектации КАК ВТ_ПодходящиеКомплектации
СГРУППИРОВАТЬ ПО
    ВТ_ПодходящиеКомплектации.Ссылка
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_ПодходящиеКомплектации.Номенклатура) = &Количество

Рекомендации по оптимизации производительности

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

  1. Передача эталона во временную таблицу: если список позиций для поиска велик (более 50–100 элементов), не передавайте его в параметр оператора В (&Массив). Загружайте данные во временную таблицу через объект МенеджерВременныхТаблиц с обязательной индексацией по соединяемым полям.
  2. Отказ от полных соединений: избегайте использования ПОЛНОЕ СОЕДИНЕНИЕ для сравнения таблиц. В СУБД полные соединения выполняются медленнее и требуют значительных ресурсов памяти по сравнению с левыми соединениями и агрегацией.
  3. Архитектурная альтернатива (хеширование состава): если операция поиска полного соответствия выполняется регулярно в реальном времени, эффективнее всего при сохранении объекта формировать упорядоченную хеш-сумму его состава (например, строку с отсортированными GUID или MD5-хеш) и сохранять ее в индексированный реквизит. В этом случае поиск сведется к простому условию равенства по индексу: ГДЕ ХешСостава = &ХешЭталона.
← На главную