В практике разработчика 1С часто возникает задача поиска объектов (документов или справочников), состав табличной части которых в точности соответствует переданному образцу. Типичный пример: найти заказ клиента с точно таким же перечнем товаров и количеств, как в текущей корзине. При малых объемах данных любое решение будет работать быстро, но когда речь идет о десятках тысяч документов, неоптимальные запросы начинают «тормозить» систему. Разберем, как реализовать этот поиск максимально эффективно.
Рассмотрим ситуацию, с которой столкнулся автор темы. Изначально задача решалась через полное соединение (FULL JOIN) или даже через декартово произведение (CROSS JOIN) с последующей фильтрацией. Проанализируем, почему это плохой путь.
Использование конструкции ПО (ИСТИНА) в левом соединении приводит к тому, что система пытается перемножить каждую строку образца с каждой строкой из всей таблицы справочника или документа. На больших объемах это вызывает катастрофическое падение производительности. Рассмотрим пример неэффективного кода, который часто пытаются использовать новички:
// Пример неоптимального подхода через CROSS JOIN
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| втТаблицаБригады.Специалист,
| БригадыСпециалисты.Ссылка КАК Ссылка1
|ПОМЕСТИТЬ втУмножение
|ИЗ
| втТаблицаБригады КАК втТаблицаБригады
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Бригады.Специалисты КАК БригадыСпециалисты
| ПО (ИСТИНА);";
Такой подход заставляет сервер СУБД обрабатывать гигантские массивы временных данных. Правильный путь — использовать логику реляционного деления, основанную на сравнении количеств и состава строк.
Давайте разберем наиболее надежный и быстрый алгоритм. Его суть заключается в следующем: если в документе-кандидате количество строк совпадает с количеством строк в образце, и при этом все строки образца присутствуют в этом документе, то такие наборы идентичны.
Выполним задачу по шагам:
Номенклатура и Количество).Проанализируем пример реализации такого запроса:
// 1. Помещаем образец в ВТ и считаем количество строк
"ВЫБРАТЬ
| Образец.Номенклатура КАК Номенклатура,
| Образец.Количество КАК Количество
|ПОМЕСТИТЬ втОбразец
|ИЗ
| &ТаблицаОбразец КАК Образец
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура,
| Количество;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| КОЛИЧЕСТВО(*) КАК ВсегоСтрок
|ПОМЕСТИТЬ втПараметры
|ИЗ
| втОбразец КАК втОбразец;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ТЧ.Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ втОбразец КАК втОбразец
| ПО ТЧ.Номенклатура = втОбразец.Номенклатура
| И ТЧ.Количество = втОбразец.Количество
|ГДЕ
| ТЧ.Ссылка.Проведен // Дополнительный фильтр для ускорения
|СГРУППИРОВАТЬ ПО
| ТЧ.Ссылка
|ИМЕЯ
| КОЛИЧЕСТВО(ТЧ.Номенклатура) = (ВЫБРАТЬ ВсегоСтрок ИЗ втПараметры)
| И КОЛИЧЕСТВО(ТЧ.Ссылка.Товары.НомерСтроки) = (ВЫБРАТЬ ВсегоСтрок ИЗ втПараметры)";
Важный нюанс: Условие в ИМЕЯ (HAVING) по количеству строк в самом документе (КОЛИЧЕСТВО(ТЧ.Ссылка.Товары.НомерСтроки)) критически важно. Без него запрос найдет и те документы, где наш образец является лишь частью (подмножеством) более крупного списка товаров.
Выясним причину возможных ошибок при наличии одинаковых строк в ТЧ. Если в документе или в образце может быть две строки с одной и той же номенклатурой и одинаковым количеством, прямое соединение «размножит» записи, и результат КОЛИЧЕСТВО() будет неверным.
Чтобы избежать этого, перед сравнением мы должны свернуть (сгруппировать) обе таблицы. Рассмотрим, как это изменить в запросе:
// Сворачиваем данные ТЧ документа перед сравнением
"ВЫБРАТЬ
| ТЧ.Ссылка КАК Ссылка,
| ТЧ.Номенклатура КАК Номенклатура,
| СУММА(ТЧ.Количество) КАК Количество
|ПОМЕСТИТЬ втСвернутаяТЧ
|ИЗ
| Документ.Продажа.Товары КАК ТЧ
|СГРУППИРОВАТЬ ПО
| ТЧ.Ссылка,
| ТЧ.Номенклатура;";
После такой подготовки алгоритм сравнения из первого решения будет работать безотказно даже при наличии дублей в исходных данных.
Проанализируем ситуацию, когда документов миллионы, а поиск нужно выполнять мгновенно. В этом случае соединение «лоб в лоб» может быть накладным. Опытные разработчики применяют технику хеширования или контрольных сумм.
Для каждой строки ТЧ можно вычислить числовой идентификатор. Например, ХешСтроки = (ВнутреннийКодНоменклатуры * Количество). Затем для каждого документа в отдельном регистре или реквизите шапки хранится СуммарныйХеш (сумма хешей всех строк). При поиске мы сначала отбираем документы, у которых СуммарныйХеш совпадает с хешем нашего образца. Это позволяет мгновенно отсечь 99.9% неподходящих документов, а оставшиеся единицы проверить детально методом из первого решения.
Подведем итог и выделим ключевые правила для эффективной работы запроса:
ИНДЕКСИРОВАТЬ ПО) по тем полям, по которым идет соединение.ПОЛНОЕ СОЕДИНЕНИЕ и ЕСТЬNULL в условиях соединения — это лишает СУБД возможности эффективно использовать индексы.ГДЕ Документ.КоличествоСтрок = &КоличествоВОбразце, что в разы ускорит запрос.Рассмотренные методы позволяют решать задачу поиска идентичных наборов данных в 1С быстро и надежно, обеспечивая стабильную работу системы даже при значительном росте объема информационной базы.