Как правильно сопоставить номенклатуру в документе с большой базой (7 млн строк) без потери производительности?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При работе с крупными информационными базами 1С программисты часто сталкиваются с задачей сопоставления небольшого набора данных (например, 300 строк в документе) с огромными справочниками, содержащими миллионы записей. Типичный пример — заполнение поля НоменклатураПоставщика в документе ЗаказПоставщику. Если справочник насчитывает 7 миллионов строк, классический перебор строк документа с выполнением запроса в цикле приведет к катастрофическому падению производительности и перегрузке сервера СУБД. Чтобы вовремя обнаружить подобные проблемы, рекомендуется регулярно проводить замер производительности контура 1С и поиск узких мест. В этой статье мы разберем, как построить оптимальный алгоритм, используя возможности языка запросов и механизмы платформы 1С.

Главное правило: отказ от запросов в цикле

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

Вместо этого мы применим подход с использованием пакетных запросов и временных таблиц. Это позволит нам передать все необходимые данные на сервер за один раз, обработать их внутри СУБД и получить готовый результат.

Шаг 1: Подготовка данных и использование временных таблиц

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

Рассмотрим пример подготовки данных в коде 1С:


// Выгружаем нужные колонки из ТЧ документа
ТаблицаДляПоиска = Объект.Товары.Выгрузить( , "Номенклатура, НомерСтроки");

// Создаем запрос
Запрос = Новый Запрос;
Запрос.МенеджерВременных Таблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЗ.Номенклатура КАК Номенклатура,
    |    ТЗ.НомерСтроки КАК НомерСтроки
    |ПОМЕСТИТЬ ВТ_ИсходныеДанные
    |ИЗ
    |    &ТаблицаДляПоиска КАК ТЗ
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ИНДЕКСИРОВАТЬ ПО
    |    Номенклатура";

Запрос.УстановитьПараметр("ТаблицаДляПоиска", ТаблицаДляПоиска);
Запрос.Выполнить();

Важный нюанс: Обратите внимание на инструкцию ИНДЕКСИРОВАТЬ ПО. Когда мы работаем с таблицей в 7 миллионов строк, индексация временной таблицы по полям связи критически важна. Это позволяет СУБД использовать эффективные алгоритмы соединения (например, Hash Join), не просматривая все записи основной таблицы.

Шаг 2: Эффективное соединение с большой таблицей

Теперь, когда наши 300 строк находятся в памяти СУБД в виде индексированной временной таблицы, мы можем выполнить соединение со справочником НоменклатураПоставщика. Выясним причину, по которой поиск может быть медленным: если мы просто соединим таблицы по номенклатуре, СУБД все равно придется анализировать огромный массив данных. Если проблема сохраняется даже при правильных запросах, стоит проанализировать SQL сервер глазами 1С-ника, чтобы исключить проблемы с планами запросов или нехваткой ресурсов.

Чтобы сузить область поиска, необходимо использовать фильтрацию по владельцу (контрагенту или партнеру). В типовых решениях (УТ 11, ERP) номенклатура поставщика всегда привязана к конкретному контрагенту. Указание этого параметра в условии ГДЕ или в условии связи мгновенно отсекает 99.9% лишних данных.

Разберем пример пакетного запроса:


Запрос.Текст = 
    "ВЫБРАТЬ
    |    ВТ.НомерСтроки,
    |    НоменклатураПоставщика.Ссылка КАК НоменклатураПоставщика
    |ИЗ
    |    ВТ_ИсходныеДанные КАК ВТ
    |        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.НоменклатураКонтрагентов КАК НоменклатураПоставщика
    |        ПО ВТ.Номенклатура = НоменклатураПоставщика.Номенклатура
    |            И НоменклатураПоставщика.Владелец = &Поставщик
    |            И (НЕ НоменклатураПоставщика.ПометкаУдаления)";

Запрос.УстановитьПараметр("Поставщик", Объект.Поставщик);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();

Шаг 3: Обработка дублей и сопоставление данных

Проанализируем ситуацию, которая часто возникает на практике: одной вашей номенклатуре может соответствовать несколько записей в базе поставщика (разные артикулы или характеристики). При использовании ЛЕВОЕ СОЕДИНЕНИЕ в результате запроса может получиться больше строк, чем было в исходном документе. Чтобы избежать "размножения" строк в заказе, мы должны решить, какую именно позицию выбрать. Для автоматизации этих процессов в сложных случаях можно использовать специализированное массовое оформление продаж с автоподбором номенклатуры и партнёров.

Рассмотрим способы решения этой проблемы:

  1. Использование группировки: Выбирать первую попавшуюся ссылку через функцию МИНИМУМ(Ссылка).
  2. Сортировка и выборка: Если у поставщика есть "основная" номенклатура, добавить фильтр по соответствующему признаку.
  3. Интерактивный выбор: Если соответствий несколько, можно выводить пользователю окно для уточнения.

Шаг 4: Заполнение табличной части документа

После получения результата запроса нам нужно максимально быстро обновить данные в документе. Поскольку у нас есть НомерСтроки, мы можем обращаться к строкам ТЧ напрямую по индексу. Помните, что индекс коллекции в 1С начинается с 0, а НомерСтроки в документе — с 1.

Посмотрим на пример кода для обновления строк:


Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
    // НомерСтроки в ТЧ соответствует (Индекс + 1)
    ТекущаяСтрока = Объект.Товары[СтрокаРезультата.НомерСтроки - 1];
    ТекущаяСтрока.НоменклатураПоставщика = СтрокаРезультата.НоменклатураПоставщика;
КонецЦикла;

Дополнительные рекомендации по оптимизации

Рассмотрим подробнее технические аспекты, которые влияют на скорость выполнения этой задачи при работе с 7 миллионами строк:

  1. Проверка индексов в метаданных: Убедитесь, что в справочнике НоменклатураПоставщика для реквизита Номенклатура установлено свойство Индексировать. Если индекса нет, СУБД будет выполнять полное сканирование таблицы (Full Table Scan), что приведет к блокировкам и длительному ожиданию.
  2. Права доступа и RLS: Если на справочники наложены сложные ограничения прав, это может существенно замедлить выборку. В некоторых случаях для ускорения формирования отчетных данных или выборок целесообразно использовать привилегированные отчеты, работающие в обход ограничений производительности RLS.
  3. Использование метода Загрузить(): Если строк в документе очень много (тысячи), иногда быстрее подготовить полную таблицу значений со всеми данными и использовать метод Объект.Товары.Загрузить(НоваяТЗ), вместо обхода в цикле.

Таким образом, мы выяснили, что правильный алгоритм заключается в передаче данных ТЧ во временную таблицу, индексации этой таблицы и последующем соединении с основным справочником с обязательной фильтрацией по владельцу. Подобные методики оптимизации работы с данными лежат в основе многих профессиональных решений, например, через них достигается ускорение формирования отчета «Валовая прибыль предприятия» в современных редакциях УТ и ERP. Это превращает потенциально тяжелую операцию в мгновенное действие, независимо от того, насколько велика база поставщика.

← На главную