Как найти элементы справочника, табличная часть которых точно соответствует заданному массиву?

Программист 1С v8.3 (Обычные формы)
← На главную

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

Метод 1: Алгоритм исключения (через временные таблицы)

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

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

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

Шаг 3: Проверка на соответствие. Используем полное соединение между составом изделия и эталонным массивом. Если после соединения мы получаем NULL со стороны эталона — значит, у изделия есть лишний элемент. Если NULL со стороны состава — значит, у изделия не хватает какого-то элемента из массива.

Рассмотрим пример кода для реализации этой логики:


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

Результат = Запрос.Выполнить().Выгрузить();

Метод 2: Оптимальный способ (Количественное сравнение)

Хотя первый метод логичен, он может быть ресурсоемким из-за ПОЛНОГО СОЕДИНЕНИЯ. Проанализируем более производительный способ, который часто называют «методом двойного счета». Выясним, как он работает:

  1. Подсчитываем количество уникальных элементов в эталонном массиве (N).
  2. Выбираем из базы те изделия, у которых количество строк в табличной части равно N.
  3. Внутри этих изделий проверяем, что количество строк, где номенклатура входит в эталонный массив, также равно N.

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


// Предположим, МассивКомплектующих содержит 3 уникальных ссылки
КоличествоЭталон = МассивКомплектующих.Количество();

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

Важный нюанс: Второе условие в ИМЕЮЩИЕ гарантирует, что в изделии нет ничего лишнего, кроме элементов из набора. Этот метод работает на порядок быстрее на больших объемах данных.

Типичные ошибки при работе с СКД и внешними наборами данных

Разберем ситуацию, когда запрос выдает ошибку Не найден внешний набор данных "НД1". Это происходит в тех случаях, когда программно создается схема компоновки данных (СКД), но нарушается связь между именами наборов в схеме и ключами в структуре внешних данных.

Проанализируем основные правила инициализации ПроцессораКомпоновкиДанных:

Пример корректной связки:


// В схеме СКД создан НаборДанныхОбъект с именем "ДанныеСостава"
СтруктураВнешнихНаборов = Новый Структура;
СтруктураВнешнихНаборов.Вставить("ДанныеСостава", ТаблицаРезультатовЗапроса);

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, СтруктураВнешнихНаборов, ДанныеРасшифровки);

Проблема XDTO и передачи данных на сервер

Рассмотрим причину ошибки Ошибка отображения типов: Отсутствует отображение для типа 'ОтчетТабличнаяЧастьСтрока...'. Эта ошибка часто ставит в тупик начинающих разработчиков. Выясним причину:

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

Как правильно:

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

Итоги и рекомендации

Для эффективного решения задачи поиска по составу мы рекомендуем использовать метод количественного сравнения, так как он наиболее лаконичен и производителен. При работе с СКД всегда проверяйте соответствие имен наборов данных, а при возникновении ошибок XDTO — убедитесь, что вы не передаете клиентские объекты на сервер.

Если ваша база данных содержит сотни тысяч изделий, а поиск должен быть мгновенным, рассмотрите возможность хеширования состава. При записи изделия можно формировать отсортированную строку из ГУИДов комплектующих и сохранять её хеш в отдельный реквизит. Тогда поиск по точному соответствию превратится в сравнение двух строк, что выполняется практически мгновенно.

← На главную