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