При работе с крупными информационными базами 1С программисты часто сталкиваются с задачей сопоставления небольшого набора данных (например, 300 строк в документе) с огромными справочниками, содержащими миллионы записей. Типичный пример — заполнение поля НоменклатураПоставщика в документе ЗаказПоставщику. Если справочник насчитывает 7 миллионов строк, классический перебор строк документа с выполнением запроса в цикле приведет к катастрофическому падению производительности и перегрузке сервера СУБД. Чтобы вовремя обнаружить подобные проблемы, рекомендуется регулярно проводить замер производительности контура 1С и поиск узких мест. В этой статье мы разберем, как построить оптимальный алгоритм, используя возможности языка запросов и механизмы платформы 1С.
Первое и самое важное правило при работе с любыми объемами данных — никогда не выполняйте запросы внутри цикла Для Каждого. Даже если в документе всего 300 строк, выполнение 300 отдельных обращений к СУБД создаст избыточную нагрузку (так называемые Roundtrips — сетевые задержки между сервером приложений и сервером базы данных). Выявить такие и другие потенциальные проблемы в коде на ранних этапах помогает автоматизированный анализ конфигураций на наличие ошибок. В случае с таблицей в 7 миллионов строк каждый такой запрос может заставлять систему выполнять поиск, что в сумме даст неприемлемое время ожидания.
Вместо этого мы применим подход с использованием пакетных запросов и временных таблиц. Это позволит нам передать все необходимые данные на сервер за один раз, обработать их внутри СУБД и получить готовый результат.
Проанализируем ситуацию: у нас есть табличная часть документа. Нам нужно получить соответствия для номенклатуры. Первым делом мы должны выгрузить табличную часть в таблицу значений, оставив только те колонки, которые нужны для поиска, и обязательно добавив НомерСтроки, чтобы потом вернуть данные на свои места.
Рассмотрим пример подготовки данных в коде 1С:
// Выгружаем нужные колонки из ТЧ документа
ТаблицаДляПоиска = Объект.Товары.Выгрузить( , "Номенклатура, НомерСтроки");
// Создаем запрос
Запрос = Новый Запрос;
Запрос.МенеджерВременных Таблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Номенклатура КАК Номенклатура,
| ТЗ.НомерСтроки КАК НомерСтроки
|ПОМЕСТИТЬ ВТ_ИсходныеДанные
|ИЗ
| &ТаблицаДляПоиска КАК ТЗ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура";
Запрос.УстановитьПараметр("ТаблицаДляПоиска", ТаблицаДляПоиска);
Запрос.Выполнить();
Важный нюанс: Обратите внимание на инструкцию ИНДЕКСИРОВАТЬ ПО. Когда мы работаем с таблицей в 7 миллионов строк, индексация временной таблицы по полям связи критически важна. Это позволяет СУБД использовать эффективные алгоритмы соединения (например, Hash Join), не просматривая все записи основной таблицы.
Теперь, когда наши 300 строк находятся в памяти СУБД в виде индексированной временной таблицы, мы можем выполнить соединение со справочником НоменклатураПоставщика. Выясним причину, по которой поиск может быть медленным: если мы просто соединим таблицы по номенклатуре, СУБД все равно придется анализировать огромный массив данных. Если проблема сохраняется даже при правильных запросах, стоит проанализировать SQL сервер глазами 1С-ника, чтобы исключить проблемы с планами запросов или нехваткой ресурсов.
Чтобы сузить область поиска, необходимо использовать фильтрацию по владельцу (контрагенту или партнеру). В типовых решениях (УТ 11, ERP) номенклатура поставщика всегда привязана к конкретному контрагенту. Указание этого параметра в условии ГДЕ или в условии связи мгновенно отсекает 99.9% лишних данных.
Разберем пример пакетного запроса:
Запрос.Текст =
"ВЫБРАТЬ
| ВТ.НомерСтроки,
| НоменклатураПоставщика.Ссылка КАК НоменклатураПоставщика
|ИЗ
| ВТ_ИсходныеДанные КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.НоменклатураКонтрагентов КАК НоменклатураПоставщика
| ПО ВТ.Номенклатура = НоменклатураПоставщика.Номенклатура
| И НоменклатураПоставщика.Владелец = &Поставщик
| И (НЕ НоменклатураПоставщика.ПометкаУдаления)";
Запрос.УстановитьПараметр("Поставщик", Объект.Поставщик);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Проанализируем ситуацию, которая часто возникает на практике: одной вашей номенклатуре может соответствовать несколько записей в базе поставщика (разные артикулы или характеристики). При использовании ЛЕВОЕ СОЕДИНЕНИЕ в результате запроса может получиться больше строк, чем было в исходном документе. Чтобы избежать "размножения" строк в заказе, мы должны решить, какую именно позицию выбрать. Для автоматизации этих процессов в сложных случаях можно использовать специализированное массовое оформление продаж с автоподбором номенклатуры и партнёров.
Рассмотрим способы решения этой проблемы:
МИНИМУМ(Ссылка).После получения результата запроса нам нужно максимально быстро обновить данные в документе. Поскольку у нас есть НомерСтроки, мы можем обращаться к строкам ТЧ напрямую по индексу. Помните, что индекс коллекции в 1С начинается с 0, а НомерСтроки в документе — с 1.
Посмотрим на пример кода для обновления строк:
Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
// НомерСтроки в ТЧ соответствует (Индекс + 1)
ТекущаяСтрока = Объект.Товары[СтрокаРезультата.НомерСтроки - 1];
ТекущаяСтрока.НоменклатураПоставщика = СтрокаРезультата.НоменклатураПоставщика;
КонецЦикла;
Рассмотрим подробнее технические аспекты, которые влияют на скорость выполнения этой задачи при работе с 7 миллионами строк:
НоменклатураПоставщика для реквизита Номенклатура установлено свойство Индексировать. Если индекса нет, СУБД будет выполнять полное сканирование таблицы (Full Table Scan), что приведет к блокировкам и длительному ожиданию.Объект.Товары.Загрузить(НоваяТЗ), вместо обхода в цикле.Таким образом, мы выяснили, что правильный алгоритм заключается в передаче данных ТЧ во временную таблицу, индексации этой таблицы и последующем соединении с основным справочником с обязательной фильтрацией по владельцу. Подобные методики оптимизации работы с данными лежат в основе многих профессиональных решений, например, через них достигается ускорение формирования отчета «Валовая прибыль предприятия» в современных редакциях УТ и ERP. Это превращает потенциально тяжелую операцию в мгновенное действие, независимо от того, насколько велика база поставщика.