Является ли использование метода Найти() в таблице значений внутри цикла запросом в цикле и как это влияет на производительность?

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

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

Поиск в памяти vs Запрос к базе данных

Для начала проясним фундаментальное различие. Метод Найти() у объекта ТаблицаЗначений работает исключительно в оперативной памяти (на стороне сервера или клиента, в зависимости от того, где выполняется код). Он не обращается к SQL-серверу напрямую. Поэтому, формально говоря, использование Найти() в цикле не является запросом в цикле.

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

Разберем разницу между Найти() и НайтиСтроки()

Многие начинающие разработчики путают эти два метода, но с точки зрения производительности между ними лежит пропасть. Проанализируем технические нюансы каждого метода:

  1. Метод Найти(): Этот метод выполняет линейный поиск. Программа просто перебирает строки одну за другой, пока не найдет подходящее значение. Главный минус в том, что Найти() не использует индексы, даже если вы их создали для таблицы. Его сложность — O(n).
  2. Метод НайтиСтроки(): В отличие от своего собрата, этот метод умеет работать с индексами. Если в таблице значений созданы индексы по соответствующим колонкам, НайтиСтроки() выполняет поиск значительно быстрее. Сложность такого поиска приближается к O(1).

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


// Подготовим таблицу данных
ТЗ_Цены = Новый ТаблицаЗначений;
ТЗ_Цены.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ_Цены.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));

// Обязательно индексируем колонку, по которой будем искать
ТЗ_Цены.Индексы.Добавить("Номенклатура");

// При поиске в цикле используем НайтиСтроки
ПараметрыОтбора = Новый Структура("Номенклатура", ТекущийТовар);
НайденныеСтроки = ТЗ_Цены.НайтиСтроки(ПараметрыОтбора);

Если НайденныеСтроки.Количество() > 0 Тогда
    Цена = НайденныеСтроки[0].Цена;
КонецЕсли;

Скрытая угроза: обращение через точку в цикле

Часто разработчики полагают, что используют "поиск в памяти", но на самом деле провоцируют сотни запросов к базе данных. Типичный пример плохой практики — использование конструкции Найти(Ссылка.Наименование) внутри цикла. Выясним причину, почему это происходит.

Когда вы обращаетесь к свойству ссылки через точку (например, ТекСсылка.Наименование), платформа 1С не просто берет строковое значение. Если этот объект еще не кэширован, платформа выполняет неявный запрос к базе данных, чтобы получить весь объект целиком. Если в вашем цикле 1000 итераций, вы получите 1000 запросов к БД. Это гораздо хуже, чем любой поиск в таблице значений.

Решение: Заранее выбирайте все нужные поля (Наименование, Артикул и т.д.) в запросе и выгружайте их в колонки таблицы значений. Работайте в цикле только с примитивными типами данных, уже находящимися в памяти.

Когда индексы могут стать обузой

Важно понимать, что индексация — это не бесплатная операция. Рассмотрим ситуации, когда создание индексов может быть неэффективным:

Использование объектов "Соответствие" для максимального ускорения

Проанализируем ситуацию: вам нужно быстро находить значение по ключу (например, цену по ссылке на номенклатуру). В этом случае использование объекта Соответствие (Map) часто оказывается эффективнее, чем ТаблицаЗначений с индексами.

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


// Создаем соответствие для быстрого доступа
ЦеныСоответствие = Новый Соответствие;
Для Каждого Стр Из ТЗ_Данных Цикл
    ЦеныСоответствие.Вставить(Стр.Номенклатура, Стр.Цена);
КонецЦикла;

// В основном цикле поиск будет мгновенным
Цена = ЦеныСоответствие.Получить(НужнаяНоменклатура);
Если Цена <> Неопределено Тогда
    // Обработка данных
КонецЕсли;

Рекомендации по работе с большими объемами данных в памяти

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

При разрастании таблицы значений до гигантских размеров процесс rphost начинает потреблять значительный объем оперативной памяти. Если памяти станет недостаточно, операционная система задействует файл подкачки (swapping) на жестком диске. В этот момент любая работа "в памяти" станет работать медленнее, чем прямой запрос к SQL-серверу, так как диск — самое узкое место системы.

Золотое правило: Если объем данных в таблице значений исчисляется сотнями тысяч строк, рассмотрите возможность использования Временных таблиц в запросе. SQL-сервер гораздо лучше оптимизирован для поиска в больших массивах данных, умеет эффективно использовать кэши и индексы на уровне страниц данных.

Подведем итог

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

  1. Используйте метод НайтиСтроки() вместо Найти() для больших таблиц.
  2. Не забывайте добавлять колонки в коллекцию Индексы перед началом массового поиска.
  3. Избегайте обращений через точку к свойствам ссылок внутри цикла — выгружайте нужные данные в таблицу заранее.
  4. Для простого поиска по ключу отдавайте предпочтение объекту Соответствие.
  5. Следите за объемом данных: память сервера конечна, и для сверхбольших выборок лучше подходят временные таблицы в запросах.
← На главную