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