Как ускорить сравнение больших табличных частей документов в 1С 7.7

Программист 1С v7.7 Торговля и дистрибуция
← На главную

При работе с большими объемами данных в устаревших системах, таких как 1С:Предприятие 7.7, разработчики часто сталкиваются с критическим падением производительности. Типичная ситуация: необходимо сравнить две таблицы (например, по 10 000 строк в каждой) и отметить совпадающие элементы. Стандартный подход с вложенными циклами может привести к тому, что программа «зависает» на часы. Рассмотрим, как оптимизировать этот процесс и сократить время обработки с часа до нескольких секунд.

Анализ проблемы производительности

Разберем типичную ошибку в алгоритме, которую часто допускают новички. Рассмотрим фрагмент кода, где одна таблица значений (ТЗ) обходится внутри другой:


Пока ТабЗнач2.ПолучитьСтроку() = 1 Цикл 
    ТабЗнач.ВыбратьСтроки();
    Пока ТабЗнач.ПолучитьСтроку() = 1 Цикл 
        Если СокрЛП(ТабЗнач.ГТИН) = СокрЛП(ТабЗнач2.ГТИН) Тогда
            ТабЗнач.УдалитьСтроку();
            Прервать;
        КонецЕсли;
    КонецЦикла;    
КонецЦикла;

Выясним причину замедления. Математическая сложность такого алгоритма составляет O(n * m). Если в обеих таблицах по 10 000 строк, системе придется выполнить до 100 000 000 (ста миллионов) сравнений строк. Ситуация усугубляется использованием функции СокрЛП() внутри цикла, которая создает дополнительную нагрузку на процессор при каждой итерации, и методом УдалитьСтроку(), который заставляет систему пересчитывать индексы оставшихся строк ТЗ.

Решение №1: Использование метода НайтиЗначение

Первый и самый простой шаг к ускорению — использование штатного метода НайтиЗначение(). Этот метод работает значительно быстрее, чем ручной перебор строк в цикле, особенно если предварительно отсортировать таблицу значений.

Проанализируем оптимизированный алгоритм:

  1. Выгружаем данные из документов в таблицы значений.
  2. Очищаем строки от лишних пробелов один раз при заполнении ТЗ, чтобы не использовать СокрЛП() в цикле.
  3. Для поиска используем НайтиЗначение().

Пример кода для эффективного поиска:


// Предварительная подготовка ТЗ
ТабЗнач.ВыбратьСтроки();
Пока ТабЗнач.ПолучитьСтроку() = 1 Цикл
    ТабЗнач.ГТИН = СокрЛП(ТабЗнач.ГТИН);
КонецЦикла;

// Основной цикл обработки документа
Док.ВыбратьСтроки();
Пока Док.ПолучитьСтроку() = 1 Цикл
    ТекГТИН = СокрЛП(Док.КодыДатаматрикс);
    НомСтр = 0;
    // Используем НайтиЗначение вместо вложенного цикла
    Если ТабЗнач.НайтиЗначение(ТекГТИН, НомСтр, "ГТИН") = 1 Тогда
        Док.Печ = 0; // Ставим пометку
    КонецЕсли;
КонецЦикла;

Важный нюанс: метод НайтиЗначение() работает гораздо эффективнее, если предварительно вызвать ТабЗнач.Сортировать("ГТИН"). В некоторых сборках платформы 7.7 это позволяет системе использовать бинарный поиск.

Решение №2: Метод слияния и сворачивания (Merge & Collapse)

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

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

  1. Создаем общую таблицу значений с колонкой для поиска (например, ГТИН) и служебной колонкой Флаг.
  2. Выгружаем данные из первого документа, заполняя Флаг значением 1.
  3. Выгружаем данные из второго документа в ту же таблицу, заполняя Флаг значением 10 (или любым другим числом, чтобы суммы не пересекались).
  4. Выполняем Свернуть("ГТИН", "Флаг").

В результате, если в строке Флаг равен 1 — значение было только в первом документе. Если 10 — только во втором. Если 11 — значение присутствует в обоих документах. Рассмотрим пример кода:


ТЗ_Общая = СоздатьОбъект("ТаблицаЗначений");
ТЗ_Общая.НоваяКолонка("ГТИН", "Строка");
ТЗ_Общая.НоваяКолонка("Флаг", "Число");

// Выгружаем данные из первого документа (Док1)
Док1.ВыгрузитьТабличнуюЧасть(ТЗ_Общая, "КодыДатаматрикс");
ТЗ_Общая.Заполнить(1, , , "Флаг");

// Создаем временную ТЗ для второго документа (Док2)
ТЗ_Врем = СоздатьОбъект("ТаблицаЗначений");
ТЗ_Врем.НоваяКолонка("ГТИН", "Строка");
ТЗ_Врем.НоваяКолонка("Флаг", "Число");
Док2.ВыгрузитьТабличнуюЧасть(ТЗ_Врем, "КодГолова");
ТЗ_Врем.Заполнить(10, , , "Флаг");

// Объединяем таблицы
ТЗ_Врем.ВыбратьСтроки();
Пока ТЗ_Врем.ПолучитьСтроку() = 1 Цикл
    ТЗ_Общая.НоваяСтрока();
    ТЗ_Общая.ГТИН = ТЗ_Врем.ГТИН;
    ТЗ_Общая.Флаг = ТЗ_Врем.Флаг;
КонецЦикла;

// Сворачиваем по ключевому полю
ТЗ_Общая.Свернуть("ГТИН", "Флаг");

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

Решение №3: Работа с транзакциями и объектами

Часто причиной «зависания» является не столько алгоритм сравнения, сколько процесс записи документа. В 1С 7.7 каждое сохранение объекта Док.Записать() инициирует проверку итогов, блокировку таблиц и запись в базу данных.

Проанализируем, как оптимизировать запись:

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


НачатьТранзакцию();
Пока Док.ПолучитьСтроку() = 1 Цикл
    // Логика сравнения...
    Если Найдено = 1 Тогда
        Док.Печ = 1;
    КонецЕсли;
КонецЦикла;
Док.Записать();
ЗафиксироватьТранзакцию();

Дополнительные советы по оптимизации

При работе с очень большими объемами (десятки и сотни тысяч строк) в 1С 7.7 программисты часто используют внешние компоненты. Одной из самых популярных является 1C++. В ней реализован объект ИндексированнаяТаблица. В отличие от стандартной ТЗ, поиск в индексированной таблице происходит практически мгновенно вне зависимости от количества строк, так как используются алгоритмы хеширования.

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

Подведем итог: для быстрого сравнения документов в 1С 7.7 следует отказаться от вложенных циклов в пользу метода НайтиЗначение() по отсортированной колонке или использовать метод объединения и сворачивания таблиц. Это позволит сократить время выполнения задачи с «времени на перекур» до нескольких мгновений.

← На главную