Как объединить и синхронизировать две таблицы значений в 1С 7.7?

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

Работа с таблицами значений (ТЗ) в платформе 1С 7.7 часто сопряжена с трудностями из-за отсутствия встроенных механизмов соединения (Join), привычных разработчикам на платформе 8.x или SQL. Рассмотрим классическую задачу: у нас есть две таблицы ТЗ1 и ТЗ2. В ТЗ2 присутствует колонка Ключ, содержащая номера строк. Нам необходимо обновить данные в ТЗ1 на основании ТЗ2, удалить лишние строки и добавить новые, ориентируясь на этот ключ.

В этой статье мы разберем несколько подходов: от стандартных алгоритмов на чистом языке 1С до использования мощных инструментов расширения платформы, таких как 1CPP.

Метод 1: Ручной перебор и промежуточная таблица

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

Разберем логику, предложенную в обсуждении, с небольшими оптимизациями для стабильности:


// Создаем вспомогательную таблицу для хранения индексов, которые должны остаться
ТЗ_Служебная = СоздатьОбъект("ТаблицаЗначений");
ТЗ_Служебная.НоваяКолонка("НомерСтрокиТЗ1");

// 1. Проходим по ТЗ2 и обновляем существующие строки в ТЗ1 или добавляем новые
ТЗ2.ВыбратьСтроки();
Пока ТЗ2.ПолучитьСтроку() = 1 Цикл
    НомерСтрокиВТЗ1 = ТЗ2.Ключ;
    
    // Пытаемся позиционироваться на нужной строке в ТЗ1
    Если ТЗ1.КоличествоСтрок() >= НомерСтрокиВТЗ1 Тогда
        ТЗ1.ПолучитьСтрокуПоНомеру(НомерСтрокиВТЗ1);
        // Заменяем значения в совпадающих колонках
        // Здесь мы предполагаем, что имена колонок известны
        ТЗ1.Товар = ТЗ2.Товар;
        ТЗ1.Количество = ТЗ2.Количество;
    Иначе
        // Если ключ больше количества строк, добавляем новую строку
        ТЗ1.НоваяСтрока();
        ТЗ1.Товар = ТЗ2.Товар;
        ТЗ1.Количество = ТЗ2.Количество;
        НомерСтрокиВТЗ1 = ТЗ1.КоличествоСтрок(); // Обновляем индекс для служебной таблицы
    КонецЕсли;
    
    // Запоминаем, что эта строка должна остаться
    ТЗ_Служебная.НоваяСтрока();
    ТЗ_Служебная.НомерСтрокиТЗ1 = НомерСтрокиВТЗ1;
КонецЦикла;

// 2. Удаляем из ТЗ1 строки, которых не было в ТЗ2.Ключ
// Важно: удаление производим с конца, чтобы не нарушать нумерацию
Для Счетчик = -ТЗ1.КоличествоСтрок() По -1 Цикл
    Ном = -Счетчик;
    ВремСтр = 0;
    Если ТЗ_Служебная.НайтиЗначение(Ном, ВремСтр, "НомерСтрокиТЗ1") = 0 Тогда
        ТЗ1.УдалитьСтроку(Ном);
    КонецЕсли;
КонецЦикла;

Важное замечание: использование оператора Перейти и меток (как в сообщении №10) считается в современной разработке "дурным тоном". Наш пример выше реализует ту же логику более безопасно с точки зрения структуры кода.

Метод 2: Использование Индексированной Таблицы (1CPP)

Наиболее эффективный и современный (для 7.7) способ — использование объекта ИндексированнаяТаблица (ИТЗ) из поставки 1CPP. Этот объект поддерживает методы ЛевоеСоединение, ВнутреннееСоединение и ПравоеСоединение, которые работают в десятки раз быстрее обычных циклов.

Рассмотрим алгоритм, который позволяет выполнить синхронизацию буквально в несколько строк кода:

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

Проанализируем пример кода на основе сообщения №14:


_ИТЗ1 = СоздатьОбъект("ИндексированнаяТаблица");
_ИТЗ1.Загрузить(ТЗ1);
_ИТЗ1.НоваяКолонка("НомерСтрокиТЗ");

// Заполняем техническую колонку индексами
_ИТЗ1.ВыбратьСтроки();
Пока _ИТЗ1.ПолучитьСтроку() = 1 Цикл
    _ИТЗ1.НомерСтрокиТЗ = _ИТЗ1.НомерСтроки;
КонецЦикла;

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

// Выполняем ПравоеСоединение. 
// Оно оставит в ИТЗ1 только те записи, которые сопоставлены с ИТЗ2 по ключу,
// и добавит те, что есть в ИТЗ2, но отсутствуют в ИТЗ1.
_ИТЗ1.ПравоеСоединение("НомерСтрокиТЗ", _ИТЗ2, "Ключ");

// Возвращаем результат в обычную ТЗ
_ИТЗ1.Выгрузить(ТЗ1);

Оптимизация процесса заполнения индексов

Как выяснилось в ходе обсуждения (сообщения №14-17), цикл по заполнению колонки НомерСтрокиТЗ может быть узким местом на очень больших таблицах. Чтобы ускорить процесс, можно использовать метод ЗаполнитьКолонку(). Рассмотрим, как подготовить список значений для мгновенного заполнения:


_аВрем = "";
Для _СчетчикЦикла = 1 По _ИТЗ1.КоличествоСтрок() Цикл
    _аВрем = _аВрем + _СчетчикЦикла + ",";
КонецЦикла;

_СписокИндексов = СоздатьОбъект("СписокЗначений");
_СписокИндексов.ИзСтрокиСРазделителями(Лев(_аВрем, СтрДлина(_аВрем) - 1));
_ИТЗ1.ЗаполнитьКолонку(, "НомерСтрокиТЗ", _СписокИндексов);

Хотя этот метод кажется сложнее из-за работы со строками, на таблицах в десятки тысяч строк он может показать лучший результат, чем прямой перебор через ПолучитьСтроку(), так как минимизирует количество обращений к контексту объекта внутри цикла.

Альтернатива: Использование 1SQLite

Если задача объединения таблиц становится еще сложнее (например, нужно объединять по нескольким условиям с агрегатными функциями), рекомендуется посмотреть в сторону компоненты 1SQLite. Она позволяет зарегистрировать таблицы значений как виртуальные таблицы в памяти и использовать чистый SQL:

Рассмотрим вкратце схему действий:

Этот подход избавляет от необходимости вручную следить за индексами и колонками, перекладывая всю работу на оптимизатор SQL-запросов.

Выводы

Мы выяснили, что для решения задачи синхронизации таблиц в 1С 7.7 существует три основных пути:

  1. Стандартный: использование циклов и удаление строк с конца таблицы. Подходит для простых задач и малых объемов данных.
  2. Профессиональный (1CPP): использование ИндексированнойТаблицы и методов соединений. Это "золотой стандарт" для высоконагруженных систем на 7.7.
  3. Максимально гибкий (SQLite): использование SQL-запросов к данным в памяти, если логика объединения выходит за рамки простого Join.

Использование ИндексированнойТаблицы с методом ПравоеСоединение позволяет сократить объем кода в несколько раз, при этом значительно повысив скорость работы алгоритма (до 150 мс на таблицах в 10 000 строк).

← На главную