В процессе разработки на платформе 1С:Предприятие перед программистом часто встает задача быстрого поиска данных внутри табличной части документа или таблицы значений. Традиционно для этого используется метод НайтиСтроки, однако существует мнение, что «запросы всегда быстрее». Разберем на практическом примере, почему попытка оптимизировать код через создание запроса к таблице значений может привести к противоположному результату — значительному замедлению работы системы.
Рассмотрим ситуацию, с которой столкнулся автор темы. В коде выполняется поиск строк в табличной части документа по двум критериям: Номенклатура и логический флаг ИтоговаяСтрока. Исходный код выглядел следующим образом:
Отбор = Новый Структура("Номенклатура, ИтоговаяСтрока", ТекДанные.Номенклатура, Ложь);
МассивСтрок = ЭтотОбъект.Товары.НайтиСтроки(Отбор);
С целью ускорения автор решил переписать этот фрагмент, используя механизм запросов. Логика проста: передать таблицу значений в запрос как параметр, поместить её во временную таблицу и выполнить отбор средствами SQL. Рассмотрим реализованный им вариант:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ." + ИмяКолонки + ",
| ТЗ.Номенклатура КАК Номенклатура,
| ТЗ.ИтоговаяСтрока КАК ИтоговаяСтрока
|ПОМЕСТИТЬ ВТЗ
|ИЗ
| &ТЗ КАК ТЗ
|;
|ВЫБРАТЬ
| ВТЗ." + ИмяКолонки + "
|ИЗ
| ВТЗ КАК ВТЗ
|ГДЕ
| ВТЗ.Номенклатура = &Номенклатура
| И НЕ ВТЗ.ИтоговаяСтрока";
Запрос.УстановитьПараметр("ТЗ", ЭтотОбъект.Товары.Выгрузить());
Запрос.УстановитьПараметр("Номенклатура", ТекДанные.Номенклатура);
ТЗИт = Запрос.Выполнить().Выгрузить();
Проанализировав результат, разработчик обнаружил, что метод Выгрузить() и последующее выполнение запроса работают намного медленнее, чем стандартный метод НайтиСтроки(). Выясним причину этого явления.
Для понимания причин замедления нам нужно рассмотреть архитектуру взаимодействия сервера приложений 1С и сервера СУБД (SQL Server). Проанализируем основные этапы выполнения запроса к переданной таблице значений:
&ТЗ, система должна подготовить эти данные для отправки. Таблица преобразуется в специальный формат (чаще всего XML), что требует времени и ресурсов процессора на сервере приложений.tempdb. Это дисковая операция, которая на порядок медленнее работы с оперативной памятью.В то же время метод НайтиСтроки() работает напрямую в оперативной памяти сервера приложений. Для небольших коллекций (до 10-20 тысяч строк) накладные расходы на работу с SQL всегда будут перекрывать любую выгоду от использования языка запросов.
Если метод НайтиСтроки() кажется вам медленным, скорее всего, проблема в том, что поиск выполняется линейно (полным перебором). Чтобы ускорить его в десятки раз, мы можем использовать индексы. Это лучший способ оптимизации, который часто игнорируют.
Проанализируем, как добавить индекс к таблице значений:
ТЗ_Товары = ЭтотОбъект.Товары.Выгрузить();
// Создаем индекс по тем колонкам, по которым будет идти отбор
ТЗ_Товары.Индексы.Добавить("Номенклатура, ИтоговаяСтрока");
// Теперь НайтиСтроки будет работать мгновенно (используя поиск по индексу)
Отбор = Новый Структура("Номенклатура, ИтоговаяСтрока", ТекДанные.Номенклатура, Ложь);
МассивСтрок = ТЗ_Товары.НайтиСтроки(Отбор);
Важное правило: Индексация имеет смысл только в том случае, если поиск в этой таблице выполняется многократно в рамках одного алгоритма (например, внутри цикла). Создание индекса ради одного единственного поиска может занять больше времени, чем сам поиск перебором.
Когда нам нужен максимально быстрый точечный поиск по ключу, рассмотрим использование объекта Соответствие. Это аналог хеш-таблицы, где поиск значения по ключу происходит за константное время (O(1)).
Посмотрим на пример реализации:
// Создаем кэш в виде Соответствия
КэшНоменклатуры = Новый Соответствие;
Для Каждого Стр Из ЭтотОбъект.Товары Цикл
Если Не Стр.ИтоговаяСтрока Тогда
КэшНоменклатуры.Вставить(Стр.Номенклатура, Стр);
КонецЕсли;
КонецЦикла;
// В дальнейшем поиск строки выполняется максимально быстро
НайденнаяСтрока = КэшНоменклатуры.Получить(ТекДанные.Номенклатура);
Этот метод незаменим при сопоставлении больших объемов данных в алгоритмах проведения документов или сложных расчетах.
Некоторые участники обсуждения предлагали делать запрос напрямую к таблицам базы данных (например, к Документ.РеализацияТоваровУслуг.Товары). Разберем, почему это критическая ошибка в контексте работы с объектом.
Если пользователь вносит изменения в табличную часть документа на форме и еще не нажал кнопку «Записать», эти данные существуют только в оперативной памяти (в объекте ЭтотОбъект). Запрос к физическим таблицам базы данных вернет старые данные, которые были записаны ранее, или не вернет ничего, если документ новый. Поэтому для обработки текущих данных объекта всегда нужно использовать либо НайтиСтроки, либо выгрузку ТЧ в запрос, понимая риски замедления.
Подводя итог нашему анализу, сформулируем правила выбора инструмента для поиска данных:
НайтиСтроки: Идеален для табличных частей документов и небольших таблиц значений (до 1000 строк).Соответствие: Используйте для создания быстрых индексов в памяти, когда нужно сопоставлять данные по уникальному ключу.Помните, что преждевременная оптимизация — корень всех бед. Всегда делайте замер производительности встроенным отладчиком 1С, прежде чем менять надежный метод НайтиСтроки на сложные конструкции с запросами.