При разработке обработок выгрузки данных из 1С в существующие шаблоны Excel программисты часто сталкиваются с проблемой производительности. Когда порядок строк в файле заказчика не совпадает с порядком в базе данных, возникает необходимость искать соответствие для каждой строки. Если делать это «в лоб» через перебор результата запроса, время выполнения операции растет в геометрической прогрессии. Разберем, как оптимизировать этот процесс и почему стандартный поиск может выдавать ошибку даже при видимом совпадении данных.
Рассмотрим ситуацию: у нас есть результат запроса и открытый лист Excel. Нам нужно для каждой строки Excel найти соответствующий код номенклатуры в данных 1С и заполнить колонки с остатками и продажами. Типичная ошибка — использование вложенного цикла или метода НайтиСледующий() прямо в процессе обхода строк файла. Проанализируем, почему это медленно:
НайтиСледующий() перемещает курсор выборки. После того как курсор дойдет до конца, его нужно каждый раз возвращать в начало методом Сбросить(), что создает колоссальную нагрузку на ресурсы.Cells(Ячейка, Колонка).Value само по себе является медленной операцией из-за взаимодействия между процессами 1С и Excel.Самый простой и эффективный способ ускорить поиск — выгрузить результат запроса в объект ТаблицаЗначений и проиндексировать колонку, по которой осуществляется поиск (например, «Код»). Индексация создает в памяти хеш-таблицу, которая позволяет находить нужную строку практически мгновенно, минуя полный перебор.
Рассмотрим пример кода для реализации этого метода:
// 1. Получаем результат запроса и выгружаем в ТЗ
ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
// 2. Индексируем колонку для быстрого поиска
ТаблицаРезультата.Индексы.Добавить("Код");
// 3. Обходим строки Excel
Для Ячейка = НачальнаяСтрока По КонечнаяСтрока Цикл
КодИзExcel = Формат(лист.Cells(Ячейка, 2).Value, "ЧЦ=11; ЧВН=; ЧГ=0");
// Используем метод Найти(), который работает мгновенно по индексу
НайденнаяСтрока = ТаблицаРезультата.Найти(КодИзExcel, "Код");
Если НайденнаяСтрока <> Неопределено Тогда
// Заполняем данные в Excel
лист.Cells(Ячейка, 5).Value = НайденнаяСтрока.КолВоНаСклдахНачалоПериода;
лист.Cells(Ячейка, 6).Value = НайденнаяСтрока.КолВоНаПроизводствеНачалоПериода;
// ... заполнение остальных полей
КонецЕсли;
КонецЦикла;
Если вам нужна максимальная скорость поиска в памяти, проанализируем использование объекта Соответствие. В 1С этот объект реализован как ассоциативный массив, где поиск по ключу происходит быстрее, чем поиск в таблице значений, так как не требует работы с индексами всей таблицы.
Разберем по шагам, как это применить:
Соответствие.
КешДанных = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
КешДанных.Вставить(Выборка.Код, Выборка);
КонецЦикла;
// Теперь в цикле по Excel поиск будет выглядеть так:
НайденныеДанные = КешДанных.Получить(КодИзExcel);
Если НайденныеДанные <> Неопределено Тогда
// Обработка данных
КонецЕсли;
Часто программисты сталкиваются с тем, что визуально коды в 1С и Excel одинаковы, но 1С их «не видит». Проанализируем основные причины:
СокрЛП() их не удаляет. Рекомендуется очищать строку: СтрЗаменить(Значение, Символы.НПП, "").лист.Cells(Ячейка, 2).Value часто возвращает тип Число (Double), если код состоит только из цифр. При преобразовании числа 50592 в строку длиной 11 знаков через Формат нужно быть уверенным, что точность совпадает до последнего знака. Лучше сначала явно привести к строке: Строка(Ячейка.Value).ЧВН= (выводить нули) настроена неверно или длина кода в базе отличается, соответствие не будет найдено.Чтобы еще больше ускорить процесс, мы рекомендуем минимизировать количество обращений к COM-объекту Excel. Вместо того чтобы записывать данные в каждую ячейку отдельно, можно подготовить данные в 1С в виде двумерного массива, а затем передать его в Excel целиком за один вызов.
Посмотрим на пример логики:
Этот подход сокращает время работы с файлом в 10–50 раз, так как основная задержка обычно вызвана именно «пингом» между 1С и приложением Excel.
Важный момент: При использовании метода НайтиСледующий() в цикле, не забывайте, что он ищет от текущей позиции курсора. Если вы ищете значения, которые в результате запроса идут «выше» текущей строки, вы их не найдете без вызова Сбросить(). Именно поэтому использование индексированной ТаблицыЗначений является «золотым стандартом» для решения подобных задач.