Как соединить две таблицы значений в 1С без использования запроса: подробное руководство

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

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

Постановка задачи и базовый алгоритм

Предположим, у нас есть две таблицы: Таблица1 и Таблица2. В каждой из них существует ключевая колонка (например, ID или Артикул), по которой необходимо сопоставить строки. Наша цель — дополнить Таблица1 данными из Таблица2. Разберем самый простой и «дубовый» способ, который часто используют начинающие разработчики.

Проанализируем шаги реализации такого алгоритма:

  1. Подготовка структуры: в первой таблице необходимо создать колонки, которые мы планируем перенести из второй таблицы.
  2. Перебор строк: организуем цикл по строкам основной таблицы.
  3. Поиск соответствия: внутри цикла ищем строку во второй таблице, где ключевое значение совпадает.
  4. Перенос данных: если строка найдена, копируем значения из колонок второй таблицы в текущую строку первой таблицы.

Посмотрим на пример кода, реализующего этот подход:


// 1. Добавляем колонки во вторую таблицу, если их там нет
Для Каждого Колонка Из Таблица2.Колонки Цикл
    Если Таблица1.Колонки.Найти(Колонка.Имя) = Неопределено Тогда
        Таблица1.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
    КонецЕсли;
КонецЦикла;

// 2. Основной цикл по первой таблице
Для Каждого Строка1 Из Таблица1 Цикл
    // Ищем строку во второй таблице по ключу "КлючСвязи"
    ПараметрыПоиска = Новый Структура("КлючСвязи", Строка1.КлючСвязи);
    НайденныеСтроки = Таблица2.НайтиСтроки(ПараметрыПоиска);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        // Берем первую найденную строку
        Строка2 = НайденныеСтроки[0];
        // 3. Заполняем значения свойств (копируем данные по именам колонок)
        ЗаполнитьЗначенияСвойств(Строка1, Строка2);
    КонецЕсли;
КонецЦикла;

Оптимизация через индексацию: почему это критично

Рассмотрим ситуацию, когда в ваших таблицах не 10 строк, а 10 000 или более. Без специальной подготовки метод НайтиСтроки или Найти будет выполнять последовательный перебор всей второй таблицы при каждой итерации первого цикла. Это создает сложность алгоритма O(n*m), что на больших объемах приведет к зависанию программы.

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

Добавим одну критически важную строку кода перед циклом:


// Индексируем колонку, по которой будет идти поиск
Таблица2.Индексы.Добавить("КлючСвязи");

// Теперь поиск через НайтиСтроки будет работать в разы быстрее
Для Каждого Строка1 Из Таблица1 Цикл
    // ... логика поиска и заполнения ...
КонецЦикла;

Использование объекта Соответствие (Hash-Join аналог)

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

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

  1. Создаем объект Соответствие.
  2. Одним циклом проходим по Таблица2 и помещаем в соответствие пары: Ключ (значение ключевой колонки) и Значение (сама строка таблицы).
  3. Затем одним циклом проходим по Таблица1, быстро извлекая данные из соответствия по ключу.

Пример реализации метода «Соответствие»:


ХешТаблица = Новый Соответствие;
Для Каждого Стр2 Из Таблица2 Цикл
    ХешТаблица.Вставить(Стр2.КлючСвязи, Стр2);
КонецЦикла;

Для Каждого Стр1 Из Таблица1 Цикл
    СтрокаИсточник = ХешТаблица.Получить(Стр1.КлючСвязи);
    Если СтрокаИсточник <> Неопределено Тогда
        ЗаполнитьЗначенияСвойств(Стр1, СтрокаИсточник);
    КонецЕсли;
КонецЦикла;

Важный нюанс: поиск в объекте Соответствие выполняется за константное время, что делает этот способ самым производительным для соединения таблиц в оперативной памяти.

Динамическое добавление колонок и обработка типов

Часто структура второй таблицы заранее неизвестна или может меняться (динамическая иерархия). В этом случае нам нужно программно подготовить Таблица1, чтобы в нее было куда записывать данные. Анализ структуры метаданных позволяет предварительно изучить состав объектов. Рассмотрим, как правильно перенести описание колонок вместе с их типами данных.

Вместо простого добавления имени, мы можем использовать коллекцию Колонки для точного копирования свойств:


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

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

Когда все-таки стоит использовать запрос?

Несмотря на то, что задача стоит «сделать без запроса», профессиональный разработчик всегда должен оценивать целесообразность этого ограничения. Если обе таблицы находятся на сервере, зачастую эффективнее поместить их во временные таблицы запроса через bm Консоль запросов и выполнить полноценный ЛЕВОЕ СОЕДИНЕНИЕ средствами СУБД.

Рассмотрим, как это выглядит в коде:


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Помещаем первую таблицу в запрос
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ1 ИЗ &ТЗ1 КАК ТЗ1";
Запрос.УстановитьПараметр("ТЗ1", Таблица1);
Запрос.Выполнить();

// Помещаем вторую таблицу
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ2 ИЗ &ТЗ2 КАК ТЗ2";
Запрос.УстановитьПараметр("ТЗ2", Таблица2);
Запрос.Выполнить();

// Делаем соединение
Запрос.Текст = 
"ВЫБРАТЬ
|	ВТ1.*,
|	ВТ2.Колонка1,
|	ВТ2.Колонка2
|ИЗ
|	ВТ1 КАК ВТ1
|		ЛЕВОЕ СОЕДИНЕНИЕ ВТ2 КАК ВТ2
|		ПО ВТ1.Ключ = ВТ2.Ключ";

ТаблицаРезультат = Запрос.Выполнить().Выгрузить();

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

Решение проблемы иерархии подразделений

В контексте обсуждения задачи переноса иерархии подразделений (например, из ЗУП в другую базу), важно помнить, что «плоское» соединение не всегда решает проблему. Если в одной базе иерархия изменилась (добавились новые уровни), простое копирование строки может потерять контекст «родителя».

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


Функция ПолучитьЦепочкуРодителей(ПодразделениеСсылка)
    Результат = Новый Массив;
    ТекущийОбъект = ПодразделениеСсылка;
    Пока ЗначениеЗаполнено(ТекущийОбъект.Родитель) Цикл
        ТекущийОбъект = ТекущийОбъект.Родитель;
        Результат.Добавить(ТекущийОбъект);
    КонецЦикла;
    Возврат Результат;
КонецФункции

Таким образом, при соединении данных вы сможете передать не только непосредственное подразделение, но и всю его структуру, что позволит принимающей базе корректно восстановить иерархию, даже если она была изменена кадровой службой.

Подведем итоги

Мы рассмотрели несколько способов соединения таблиц значений. Выбор конкретного метода зависит от ваших условий:

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

← На главную