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

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

В практике разработки на платформе 1С:Предприятие 8 часто возникает ситуация, когда замер производительности показывает значительные временные затраты (вплоть до 14-20%) на выполнение простой, казалось бы, функции Строка(Выборка.Ссылка). Особенно остро проблема стоит в тех случаях, когда основной текст запроса находится в закрытом модуле или типовом механизме, который крайне нежелательно или невозможно редактировать. Проанализируем, почему это происходит, и разберем по шагам эффективные методы оптимизации.

Выясним причину: почему функция Строка() работает медленно?

Разберем внутренние механизмы платформы. Когда мы вызываем Строка(Ссылка) в цикле обхода выборки, система для каждой итерации может выполнять отдельный скрытый запрос к базе данных. Это классическая проблема «N+1 запросов». Если в выборке 1000 строк, платформа сделает 1 основной запрос и 1000 дополнительных для получения названия каждого объекта. Ситуация усугубляется, если в конфигурации для данного объекта (например, справочника Номенклатура) переопределен обработчик ОбработкаПолученияПредставления в модуле менеджера. В этом случае на каждую строку выборки будет запускаться программный код на встроенном языке, что создает колоссальную нагрузку на сервер приложений.

Рассмотрим также разницу между Строка(Ссылка) и обращением Ссылка.Наименование. Несмотря на кажущуюся схожесть, они работают по-разному. Обращение «через точку» заставляет платформу считать весь объект целиком и поместить его в кэш. Если у объекта десятки реквизитов, это приведет к избыточному потреблению оперативной памяти и сетевого трафика.

Решение 1. Метод предварительной подкачки данных через Соответствие

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

Разберем этот алгоритм по шагам:

  1. Сначала пройдемся по результату исходной выборки и соберем все уникальные ссылки в массив.
  2. Выполним свой собственный запрос к базе данных, который получит представления для всех ссылок из массива за один раз.
  3. Сохраним результат во временный объект Соответствие, где ключом будет сама ссылка, а значением — её строковое представление.
  4. В основном цикле заменим вызов функции Строка() на получение значения из нашего Соответствия.

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


// 1. Собираем уникальные ссылки из выборки (или ТЗ)
МассивСсылок = Новый Массив;
Пока ВыборкаТовар.Следующий() Цикл
    МассивСсылок.Добавить(ВыборкаТовар.Номенклатура);
КонецЦикла;

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

ЗапросПредставлений.УстановитьПараметр("МассивСсылок", МассивСсылок);
РезультатЗапроса = ЗапросПредставлений.Выполнить();

// 3. Заполняем соответствие для быстрого поиска в памяти
КэшПредставлений = Новый Соответствие;
ВыборкаП = РезультатЗапроса.Выбрать();
Пока ВыборкаП.Следующий() Цикл
    КэшПредставлений.Вставить(ВыборкаП.Ссылка, ВыборкаП.Представление);
КонецЦикла;

// 4. Используем кэш в основном рабочем цикле
ВыборкаТовар.Сбросить(); // если применимо
Пока ВыборкаТовар.Следующий() Цикл
    ТекПредставление = КэшПредставлений[ВыборкаТовар.Номенклатура];
    // Если в кэше нет (например, ссылка пустая), используем стандарт
    Если ТекПредставление = Неопределено Тогда
        ТекПредставление = Строка(ВыборкаТовар.Номенклатура);
    КонецЕсли;
    // ... дальнейшая логика
КонецЦикла;

Решение 2. Использование временных таблиц (ТЗ в Запрос)

Проанализируем ситуацию, когда данных очень много. В этом случае эффективнее будет не передавать массив ссылок в параметр запроса, а выгрузить результат первого запроса в ТаблицаЗначений и использовать её как временную таблицу в новом пакете запросов. Это позволит нам на уровне SQL соединить данные и получить результат функцией ПРЕДСТАВЛЕНИЕ().

Рассмотрим подробнее, почему это быстрее. Функция ПРЕДСТАВЛЕНИЕ() в языке запросов 1С оптимизирована на уровне платформы. Она подставляет нужные JOIN к таблицам справочников в итоговый SQL-запрос, и данные приходят от сервера БД уже в готовом строковом виде. Это исключает лишние серверные вызовы при обходе коллекции.

Пример схемы действий:

  1. Выгружаем результат «недоступного» запроса в ТаблицаЗначений.
  2. Создаем новый Запрос и передаем туда эту таблицу как параметр.
  3. В тексте запроса помещаем таблицу во временную таблицу (ВТ), индексируем поле со ссылкой.
  4. Соединяем ВТ с основной таблицей справочника и выбираем поле ПРЕДСТАВЛЕНИЕ.

Решение 3. Обращение «через точку» — когда это оправдано?

Существует мнение, что обращение ВыборкаТовар.Номенклатура.Наименование может работать быстрее, чем Строка(ВыборкаТовар.Номенклатура). Проанализируем этот момент. Если у справочника Номенклатура не определены специальные «Поля представления» в конфигураторе и не написан сложный код в ОбработкаПолученияПредставления, то Строка() фактически и будет пытаться получить только наименование.

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

Сравнительный итог методов

Посмотрим на эффективность предложенных методов:

Таким образом, даже если вы столкнулись с ограничением на изменение текста запроса, использование кэширования через Соответствие или вторичного запроса по списку ссылок позволит вам сократить те самые 14% нагрузки практически до нуля, значительно ускорив работу вашего решения.

← На главную