Как оптимизировать поиск в табличной части: выбираем между «НайтиСтроки», запросом и индексами

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

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

Анализ исходной задачи и попытка оптимизации

Рассмотрим ситуацию, с которой столкнулся автор темы. В коде выполняется поиск строк в табличной части документа по двум критериям: Номенклатура и логический флаг ИтоговаяСтрока. Исходный код выглядел следующим образом:


Отбор = Новый Структура("Номенклатура, ИтоговаяСтрока", ТекДанные.Номенклатура, Ложь); 
МассивСтрок = ЭтотОбъект.Товары.НайтиСтроки(Отбор);

С целью ускорения автор решил переписать этот фрагмент, используя механизм запросов. Логика проста: передать таблицу значений в запрос как параметр, поместить её во временную таблицу и выполнить отбор средствами SQL. Рассмотрим реализованный им вариант:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЗ." + ИмяКолонки + ",
    |	ТЗ.Номенклатура КАК Номенклатура,
    |	ТЗ.ИтоговаяСтрока КАК ИтоговаяСтрока
    |ПОМЕСТИТЬ ВТЗ
    |ИЗ
    |	&ТЗ КАК ТЗ
    |;
    |ВЫБРАТЬ
    |	ВТЗ." + ИмяКолонки + "
    |ИЗ
    |	ВТЗ КАК ВТЗ
    |ГДЕ
    |	ВТЗ.Номенклатура = &Номенклатура 
    |	И НЕ ВТЗ.ИтоговаяСтрока";

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

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

Почему запрос к таблице значений работает медленно?

Для понимания причин замедления нам нужно рассмотреть архитектуру взаимодействия сервера приложений 1С и сервера СУБД (SQL Server). Проанализируем основные этапы выполнения запроса к переданной таблице значений:

  1. Сериализация данных: Когда мы передаем таблицу значений (или выгруженную ТЧ) в параметр &ТЗ, система должна подготовить эти данные для отправки. Таблица преобразуется в специальный формат (чаще всего XML), что требует времени и ресурсов процессора на сервере приложений.
  2. Передача по сети: Сериализованный объем данных передается с сервера приложений на сервер SQL. Если в таблице сотни или тысячи строк, объем данных может быть существенным.
  3. Создание временной таблицы: SQL Server получает данные, десериализует их и физически записывает в базу tempdb. Это дисковая операция, которая на порядок медленнее работы с оперативной памятью.
  4. Выполнение запроса и возврат: Только после этого выполняется сам поиск, и результат снова совершает путь от SQL к серверу приложений.

В то же время метод НайтиСтроки() работает напрямую в оперативной памяти сервера приложений. Для небольших коллекций (до 10-20 тысяч строк) накладные расходы на работу с SQL всегда будут перекрывать любую выгоду от использования языка запросов.

Способ №1: Индексация таблиц значений — «серебряная пуля»

Если метод НайтиСтроки() кажется вам медленным, скорее всего, проблема в том, что поиск выполняется линейно (полным перебором). Чтобы ускорить его в десятки раз, мы можем использовать индексы. Это лучший способ оптимизации, который часто игнорируют.

Проанализируем, как добавить индекс к таблице значений:


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

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

Важное правило: Индексация имеет смысл только в том случае, если поиск в этой таблице выполняется многократно в рамках одного алгоритма (например, внутри цикла). Создание индекса ради одного единственного поиска может занять больше времени, чем сам поиск перебором.

Способ №2: Использование объекта «Соответствие» (Map)

Когда нам нужен максимально быстрый точечный поиск по ключу, рассмотрим использование объекта Соответствие. Это аналог хеш-таблицы, где поиск значения по ключу происходит за константное время (O(1)).

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


// Создаем кэш в виде Соответствия
КэшНоменклатуры = Новый Соответствие;
Для Каждого Стр Из ЭтотОбъект.Товары Цикл
    Если Не Стр.ИтоговаяСтрока Тогда
        КэшНоменклатуры.Вставить(Стр.Номенклатура, Стр);
    КонецЕсли;
КонецЦикла;

// В дальнейшем поиск строки выполняется максимально быстро
НайденнаяСтрока = КэшНоменклатуры.Получить(ТекДанные.Номенклатура);

Этот метод незаменим при сопоставлении больших объемов данных в алгоритмах проведения документов или сложных расчетах.

Проблема «грязного» чтения при запросах к БД

Некоторые участники обсуждения предлагали делать запрос напрямую к таблицам базы данных (например, к Документ.РеализацияТоваровУслуг.Товары). Разберем, почему это критическая ошибка в контексте работы с объектом.

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

Выводы и рекомендации

Подводя итог нашему анализу, сформулируем правила выбора инструмента для поиска данных:

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

← На главную