В практике разработки на платформе 1С часто возникает задача поиска объектов (документов комплектации, спецификаций номенклатуры, наборов), табличная часть которых строго совпадает с заданным набором элементов. Например, если в базе есть комплект А с составом (Т1, Т2), комплект Б с составом (Т1, Т2, Т3) и комплект С с составом (Т1, Т3), при поиске по эталону (Т1, Т2) в выборку должен попасть исключительно комплект А. Комплект Б включать нельзя, так как в нем присутствует лишняя позиция Т3.
В теории реляционных баз данных такая операция называется точным реляционным делением (Exact Relational Division). Для ее выполнения необходимо одновременно проверить два условия: все элементы эталона входят в состав проверяемого объекта, и общее количество уникальных позиций объекта строго равно количеству позиций в эталоне. Разберем основные способы реализации этой логики на языке запросов 1С.
Рассмотрим наиболее компактный и производительный вариант. Передадим эталонный список позиций во временную таблицу и соединим ее с табличной частью проверяемых объектов с помощью левого соединения. Далее сгруппируем данные по объекту и проверим совпадение мощностей множеств в секции ИМЕЮЩИЕ.
Разберем по шагам логику этого запроса:
ВТОбразец и вычисляем количество элементов в эталоне (параметр &КоличествоОбразец).ЛЕВОЕ СОЕДИНЕНИЕ таблицы комплектующих с образцом по полю Номенклатура.ИМЕЮЩИЕ проверяем: количество совпавших позиций с эталоном должно быть равно общему количеству строк в комплектации, а также равно числу элементов в переданном образце.Посмотрим на пример текста запроса:
ВЫБРАТЬ
Комплектация.Ссылка КАК Ссылка
ИЗ
Документ.КомплектацияНоменклатуры.Комплектующие КАК Комплектация
ЛЕВОЕ СОЕДИНЕНИЕ ВТОбразец КАК Образец
ПО (Образец.Номенклатура = Комплектация.Номенклатура)
ГДЕ
Комплектация.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
Комплектация.Ссылка
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Комплектация.Номенклатура) = КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Образец.Номенклатура)
И КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Образец.Номенклатура) = &КоличествоОбразец
Обратите внимание: использование конструкции КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) защищает результат от дублирования строк в табличных частях. Если в прикладной модели уникальность строк гарантирована, ключевое слово РАЗЛИЧНЫЕ можно опустить для снижения нагрузки на СУБД.
Проанализируем алгоритм, при котором проверка делится на несколько последовательных шагов. Этот метод более нагляден при отладке и позволяет независимо валидировать несколько табличных частей (например, состав исходных комплектующих и состав выходных изделий в спецификациях).
Разберем этапы реализации:
ЕСТЬ NULL находим те спецификации, в которых присутствуют номенклатурные позиции, отсутствующие в отборе (выявляем лишние строки).Посмотрим на практический пример запроса для спецификаций:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_Отбор
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка В(&МассивНоменклатуры)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_Отбор.Номенклатура) КАК Количество
ПОМЕСТИТЬ ВТ_КоличествоЭталон
ИЗ
ВТ_Отбор КАК ВТ_Отбор
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Спецификация.Ссылка КАК Ссылка,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Спецификация.Номенклатура) КАК Количество
ПОМЕСТИТЬ ВТ_СпецификацииВсе
ИЗ
Справочник.СпецификацииНоменклатуры.ИсходныеКомплектующие КАК Спецификация
СГРУППИРОВАТЬ ПО
Спецификация.Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_СпецификацииВсе.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_КандидатыПоКоличеству
ИЗ
ВТ_СпецификацииВсе КАК ВТ_СпецификацииВсе,
ВТ_КоличествоЭталон КАК ВТ_КоличествоЭталон
ГДЕ
ВТ_СпецификацииВсе.Количество = ВТ_КоличествоЭталон.Количество
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
Спецификация.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ИсключаемыеСпецификации
ИЗ
Справочник.СпецификацииНоменклатуры.ИсходныеКомплектующие КАК Спецификация
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Отбор КАК ВТ_Отбор
ПО (ВТ_Отбор.Номенклатура = Спецификация.Номенклатура)
ГДЕ
Спецификация.Ссылка В
(ВЫБРАТЬ
ВТ_КандидатыПоКоличеству.Ссылка
ИЗ
ВТ_КандидатыПоКоличеству)
И ВТ_Отбор.Номенклатура ЕСТЬ NULL
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_КандидатыПоКоличеству.Ссылка КАК Ссылка
ИЗ
ВТ_КандидатыПоКоличеству КАК ВТ_КандидатыПоКоличеству
ГДЕ
НЕ ВТ_КандидатыПоКоличеству.Ссылка В
(ВЫБРАТЬ
ВТ_ИсключаемыеСпецификации.Ссылка
ИЗ
ВТ_ИсключаемыеСпецификации)
Рассмотрим еще один лаконичный вариант, когда на первом шаге отсекаются все комплектации, содержащие хотя бы один элемент не из списка, а на втором шаге проверяется совпадение количества элементов.
Посмотрим на структуру такого запроса:
ВЫБРАТЬ
К1.Ссылка КАК Ссылка,
К1.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ПодходящиеКомплектации
ИЗ
Документ.КомплектацияНоменклатуры.Комплектующие КАК К1
ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
К0.Ссылка КАК Ссылка
ИЗ
Документ.КомплектацияНоменклатуры.Комплектующие КАК К0
ГДЕ
НЕ К0.Номенклатура В (&НаборНоменклатуры)) КАК К2
ПО К1.Ссылка = К2.Ссылка
ГДЕ
К2.Ссылка ЕСТЬ NULL
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_ПодходящиеКомплектации.Ссылка КАК Ссылка
ИЗ
ВТ_ПодходящиеКомплектации КАК ВТ_ПодходящиеКомплектации
СГРУППИРОВАТЬ ПО
ВТ_ПодходящиеКомплектации.Ссылка
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_ПодходящиеКомплектации.Номенклатура) = &Количество
Проанализируем ключевые моменты, влияющие на скорость выполнения подобных запросов в высоконагруженных базах данных:
В (&Массив). Загружайте данные во временную таблицу через объект МенеджерВременныхТаблиц с обязательной индексацией по соединяемым полям.ПОЛНОЕ СОЕДИНЕНИЕ для сравнения таблиц. В СУБД полные соединения выполняются медленнее и требуют значительных ресурсов памяти по сравнению с левыми соединениями и агрегацией.ГДЕ ХешСостава = &ХешЭталона.