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

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

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

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

Способ 1. Двухфакторный отбор по количеству элементов (оптимальный для запроса)

Для того чтобы множество элементов табличной части точно совпадало с переданным образцом, должны выполняться два условия одновременно:

  1. Общее количество уникальных строк в табличной части объекта должно быть строго равно количеству элементов в образце.
  2. Количество элементов табличной части, совпавших с образцом при внутреннем соединении, также должно быть равно количеству элементов в образце.

Рассмотрим по шагам реализацию запроса с использованием временных таблиц:


// Подготовка параметров
МассивЭлементов = Новый Массив;
МассивЭлементов.Добавить(Элемент1);
МассивЭлементов.Добавить(Элемент2);

ТаблицаОбразца = Новый ТаблицаЗначений;
ТаблицаОбразца.Колонки.Добавить("Значение");
Для Каждого Элм Из МассивЭлементов Цикл
    ТаблицаОбразца.Добавить().Значение = Элм;
КонецЦикла;

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

Запрос.УстановитьПараметр("ТаблицаОбразца", ТаблицаОбразца);
Запрос.УстановитьПараметр("КоличествоЭлементов", ТаблицаОбразца.Количество());

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

Посмотрим на логику выполнения этого пакетного запроса:

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

Способ 2. Поиск через полное соединение и проверку на NULL

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

Разберем пример запроса:


ВЫБРАТЬ
	Спр.Ссылка КАК Ссылка
ИЗ
	Справочник.Номенклатура КАК Спр
	ЛЕВОЕ СОЕДИНЕНИЕ (
		ВЫБРАТЬ
			СпрТЧ.Ссылка КАК Ссылка,
			СпрТЧ.Значение КАК ЗначениеОбъекта,
			Образец.Значение КАК ЗначениеОбразца
		ИЗ
			Справочник.Номенклатура.Состав КАК СпрТЧ
			ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Образец КАК Образец
				ПО СпрТЧ.Значение = Образец.Значение
		ГДЕ
			СпрТЧ.Значение ЕСТЬ NULL
			ИЛИ Образец.Значение ЕСТЬ NULL
	) КАК Несовпадения
		ПО Спр.Ссылка = Несовпадения.Ссылка
ГДЕ
	Несовпадения.Ссылка ЕСТЬ NULL

Данный подход хорошо работает на небольших объемах данных, однако на больших таблицах (от десятков тысяч строк) полное соединение может быть медленнее, чем предварительная фильтрация по размеру множества.

Оптимизация при высоких нагрузках: Хеширование состава

Если поиск с точным совпадением табличной части выполняется часто в высоконагруженной системе (например, подбор аналогов или поиск дубликатов спецификаций), выполнение агрегатных запросов в реальном времени создает значительную нагрузку на сервер СУБД.

В таких случаях рекомендуется использовать архитектурную оптимизацию с вычислением сигнатуры (хеша) состава:

  1. В объект добавляется строковый реквизит ХешСостава с включенным свойством Индексировать.
  2. В событии ПередЗаписью модуля объекта значения табличной части выгружаются, сортируются по возрастанию идентификатора (GUID или Код), конкатенируются в единую строку и от нее вычисляется хеш-сумма (например, с помощью объекта ХешированиеДанных и алгоритма ХешФункция.MD5 или SHA256).
  3. Поиск сводится к мгновенному запросу по индексу: ГДЕ ХешСостава = &ИскомыйХеш.

Важные нюансы при работе с табличными частями в 1С

При реализации алгоритмов сравнения множеств необходимо учитывать специфику платформы:

  1. Дублирование строк: если в табличной части возможны повторяющиеся элементы, использование функции КОЛИЧЕСТВО(*) приведет к ошибкам. Всегда следует явно использовать КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...).
  2. Индексация физических таблиц: стандартный кластерный индекс табличной части в СУБД строится по полям (Ссылка, НомерСтроки). Поиск по значениям реквизитов ТЧ без ограничения по ссылкам приводит к сканированию всей таблицы. Поэтому предварительный сбор кандидатов во временную таблицу критически важен для производительности.
← На главную