Как реализовать отбор на точное совпадение состава табличной части со списком значений в СКД?

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

При разработке отчетов на Системе компоновки данных (СКД) или написании сложных запросов в 1С программисты часто сталкиваются с задачей фильтрации данных по табличным частям. Самый распространенный сценарий — отбор по вхождению: например, найти все спецификации, где содержится определенная номенклатура. Это легко решается простым условием ГДЕ Номенклатура В (&СписокОтбора).

Однако на собеседованиях уровня Middle/Senior или в реальной практике автоматизации производства (например, при поиске аналогов спецификаций или рецептур) возникает гораздо более сложная задача: найти объекты, состав табличной части которых ПОЛНОСТЬЮ совпадает с переданным списком отбора (то есть совпадение множеств «один в один» — ни больше, ни меньше).

Обычное условие В (&СписокОтбора) здесь не сработает, так как оно вернет спецификации, где есть хотя бы один элемент из списка, или спецификации, содержащие «лишние» элементы, которых в отборе не было. В этой статье мы подробно разберем математическую суть проблемы и рассмотрим три надежных способа ее реализации в запросах 1С и СКД.

В чем главная техническая сложность задачи?

Для того чтобы доказать полную эквивалентность двух множеств (реального состава табличной части ВыходныеИзделия конкретной спецификации и пользовательского списка отбора &СписокОтбора), нам необходимо одновременно выполнить три условия:

  1. Отсутствие лишних элементов: в табличной части спецификации не должно быть ни одной номенклатуры, которой нет в списке отбора.
  2. Отсутствие недостающих элементов: в списке отбора не должно быть ни одной номенклатуры, которой нет в табличной части спецификации.
  3. Совпадение мощности множеств: количество уникальных элементов в табличной части должно быть в точности равно количеству уникальных элементов в списке отбора.

Проанализируем детально три основных способа реализации этой логики на языке запросов 1С.

---

Способ 1: Сравнение по количеству элементов (наиболее популярный)

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

Для реализации этого способа нам понадобится передать в параметры запроса не только сам список номенклатуры &СписокОтбора, но и числовое значение — количество уникальных элементов в этом списке (параметр &КоличествоОтбора).

Рассмотрим по шагам структуру запроса:

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

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


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

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

// Шаг 3: Соединяем результаты и фильтруем по точному совпадению количеств
ВЫБРАТЬ
    ВТ_Совпавшие.Ссылка КАК Ссылка
ИЗ
    ВТ_Совпавшие КАК ВТ_Совпавшие
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ВсегоВТЧ КАК ВТ_ВсегоВТЧ
    ПО ВТ_Совпавшие.Ссылка = ВТ_ВсегоВТЧ.Ссылка
ГДЕ
    ВТ_Совпавшие.КолСовпало = ВТ_ВсегоВТЧ.КолВсего
    И ВТ_Совпавшие.КолСовпало = &КоличествоОтбора

Преимущества: простой для понимания алгоритм, высокая скорость работы на средних объемах данных.

Недостатки: требуется программно вычислять и передавать параметр &КоличествоОтбора, что усложняет чисто декларативную настройку отчета в СКД без использования кода в модуле.

---

Способ 2: Полное внешнее соединение (академически верный)

Этот метод не требует передачи количества элементов в качестве отдельного параметра. Он основан на реляционной алгебре и механизме ПОЛНОЕ СОЕДИНЕНИЕ. Если мы полностью соединим реальный состав спецификации с «эталонным» списком отбора, то при идеальном совпадении у нас не должно появиться ни одного значения NULL.

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

  1. Помещаем переданный список отбора во временную таблицу ВТ_Отбор.
  2. Выбираем список спецификаций, которые имеют хотя бы одно пересечение с отбором (это нужно для оптимизации, чтобы не анализировать заведомо неподходящие данные). Назовем их «активными».
  3. Для каждой активной спецификации строим декартово произведение (кросс-соединение) со списком отбора. Это дает нам «эталонную матрицу» — то, как должна выглядеть спецификация в случае идеального совпадения.
  4. Выполняем ПОЛНОЕ СОЕДИНЕНИЕ эталонной матрицы с реальной табличной частью спецификации по полям Ссылка и Номенклатура.
  5. Если в результате соединения поле эталона или поле реальной таблицы оказывается NULL, это сигнализирует об ошибке (лишний или недостающий элемент). Такие спецификации мы отсекаем.

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


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

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

// Шаг 3: Формируем эталонную матрицу (каждая активная спецификация должна содержать весь отбор)
ВЫБРАТЬ
    ВТ_АктивныеСпецификации.Ссылка КАК Ссылка,
    ВТ_Отбор.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_Эталон
ИЗ
    ВТ_АктивныеСпецификации КАК ВТ_АктивныеСпецификации
    КРОСС СОЕДИНЕНИЕ ВТ_Отбор КАК ВТ_Отбор
;

// Шаг 4: Полное соединение эталона и реальной ТЧ
ВЫБРАТЬ
    ЕСТЬNULL(ВТ_Эталон.Ссылка, ВТ_РеальныйСостав.Ссылка) КАК Ссылка,
    ВТ_Эталон.Номенклатура КАК НоменклатураЭталон,
    ВТ_РеальныйСостав.Номенклатура КАК НоменклатураРеал
ПОМЕСТИТЬ ВТ_Сравнение
ИЗ
    ВТ_Эталон КАК ВТ_Эталон
    ПОЛНОЕ СОЕДИНЕНИЕ Справочник.СпецификацииНоменклатуры.ВыходныеИзделия КАК ВТ_РеальныйСостав
    ПО ВТ_Эталон.Ссылка = ВТ_РеальныйСостав.Ссылка
    И ВТ_Эталон.Номенклатура = ВТ_РеальныйСостав.Номенклатура
;

// Шаг 5: Выявляем спецификации с несовпадениями состава
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВТ_Сравнение.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Ошибочные
ИЗ
    ВТ_Сравнение КАК ВТ_Сравнение
ГДЕ
    ВТ_Сравнение.НоменклатураЭталон ЕСТЬ NULL
    ИЛИ ВТ_Сравнение.НоменклатураРеал ЕСТЬ NULL
;

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

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

Недостатки: кросс-соединение (декартово произведение) больших таблиц может негативно сказаться на производительности системы. Метод отлично подходит, если список отбора невелик (до нескольких десятков элементов).

---

Способ 3: Метод двойного отрицания (Double Not Exists)

Классический подход теории баз данных. Мы утверждаем, что спецификация нам подходит, если:

В языке запросов 1С это реализуется через два левых соединения с проверкой на ЕСТЬ NULL:


// Шаг 1: Получаем список спецификаций, у которых есть "лишние" элементы
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ТЧ.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ЕстьЛишние
ИЗ
    Справочник.СпецификацииНоменклатуры.ВыходныеИзделия КАК ТЧ
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Список
    ПО ТЧ.Номенклатура = Список.Ссылка И Список.Ссылка В (&СписокОтбора)
ГДЕ
    Список.Ссылка ЕСТЬ NULL
;

// Шаг 2: Находим спецификации, у которых "не хватает" элементов из отбора
// Для этого строим декартово произведение уникальных спецификаций и отбора,
// а затем левым соединением проверяем фактическое наличие.
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ТЧ_Все.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_УникальныеСпецификации
ИЗ
    Справочник.СпецификацииНоменклатуры.ВыходныеИзделия КАК ТЧ_Все
;

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

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

// Шаг 3: Итоговый отбор
ВЫБРАТЬ
    ВТ_УникальныеСпецификации.Ссылка КАК Ссылка
ИЗ
    ВТ_УникальныеСпецификации КАК ВТ_УникальныеСпецификации
ГДЕ
    НЕ ВТ_УникальныеСпецификации.Ссылка В (ВЫБРАТЬ ВТ_ЕстьЛишние.Ссылка ИЗ ВТ_ЕстьЛишние)
    И НЕ ВТ_УникальныеСпецификации.Ссылка В (ВЫБРАТЬ ВТ_ЕстьНедостача.Ссылка ИЗ ВТ_ЕстьНедостача)

---

Особенности реализации задачи в СКД

При переносе данных алгоритмов в СКД важно учитывать динамику формирования параметров. Если вы используете Способ 1, количество элементов в отборе нужно вычислять «на лету».

Сделать это можно в модуле объекта при компоновке результата отчета. Проанализируем пример кода, который перехватывает настройки СКД, считает элементы в списке отбора и заполняет параметр &КоличествоОтбора:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // Получаем настройки отчета
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    
    // Ищем наш параметр "СписокОтбора" в настройках
    ПараметрСписок = Настройки.ПараметрыДанных.Элементы.Найти("СписокОтбора");
    Если ПараметрСписок <> Неопределено И ПараметрСписок.Использование Тогда
        СписокЗначений = ПараметрСписок.Значение;
        
        // Считаем количество уникальных элементов
        КоличествоЭлементов = 0;
        Если ТипЗнч(СписокЗначений) = Тип("СписокЗначений") Тогда
            КоличествоЭлементов = СписокЗначений.Количество();
        ИначеЕсли ТипЗнч(СписокЗначений) = Тип("Массив") Тогда
            КоличествоЭлементов = СписокЗначений.Количество();
        КонецЕсли;
        
        // Устанавливаем параметр количества
        ПараметрКол = Настройки.ПараметрыДанных.Элементы.Найти("КоличествоОтбора");
        Если ПараметрКол <> Неопределено Тогда
            ПараметрКол.Значение = КоличествоЭлементов;
            ПараметрКол.Использование = Истина;
        КонецЕсли;
    КонецЕсли;
    
    // Инициализируем компоновщик макета и выполняем компоновку
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
КонецПроцедуры

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

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

← На главную