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

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

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

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

Постановка задачи

Рассмотрим исходные данные. Пусть Таблица 1 содержит колонки Серия, Характеристика и Цена. Таблица 2 содержит колонки Серия, Характеристика и Количество. Нам нужно получить таблицу, где для каждой строки из Таблицы 2 будет добавлена соответствующая цена из Таблицы 1, ориентируясь на равенство полей Серия и Характеристика.

Способ 1. Простой перебор вложенными циклами

Самый очевидный, но не самый эффективный метод — это использование вложенных циклов. Давайте проанализируем, как это работает. Мы обходим каждую строку второй таблицы и для каждой такой строки запускаем полный обход первой таблицы в поисках совпадения.

Разберем пример кода:


// Добавляем колонку для цены во вторую таблицу
Таблица2.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число", 15, 2));

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

Важное замечание: Этот метод имеет алгоритмическую сложность O(N * M). Если в каждой таблице по 10 000 строк, системе придется выполнить до 100 000 000 сравнений. Это крайне медленно, поэтому такой способ допустим только для очень маленьких таблиц (до 100-200 строк).

Способ 2. Оптимизированный поиск через метод НайтиСтроки

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

Проанализируем правильный алгоритм работы с этим методом:

  1. Добавляем колонку Цена в целевую таблицу.
  2. Создаем индекс по ключевым полям в исходной таблице (где ищем цену).
  3. В цикле выполняем поиск.

// 1. Подготовка структуры
Таблица2.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число", 15, 2));

// 2. Создание индекса — это ускорит поиск в десятки раз
Таблица1.Индексы.Добавить("Серия, Характеристика");

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

Метод НайтиСтроки при наличии индекса работает значительно быстрее прямого перебора, так как система использует внутренние механизмы хэширования или деревьев поиска.

Способ 3. Использование объекта Соответствие (Map)

Если нам нужно максимально быстро объединить таблицы без использования механизмов запросов, использование объекта Соответствие является профессиональным стандартом. Мы один раз обходим таблицу с ценами и формируем в памяти «карту», где ключом будет комбинация ключевых полей, а значением — цена.

Рассмотрим, как это реализовать по шагам:


// Создаем соответствие для хранения цен
КартаЦен = Новый Соответствие;

// Заполняем карту
Для Каждого Стр1 Из Таблица1 Цикл
    // Формируем уникальный ключ (можно использовать структуру или строку)
    Ключ = Стр1.Серия + "|" + Стр1.Характеристика; 
    КартаЦен.Вставить(Ключ, Стр1.Цена);
КонецЦикла;

// Добавляем колонку и заполняем данные
Таблица2.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число", 15, 2));

Для Каждого Стр2 Из Таблица2 Цикл
    КлючПоиска = Стр2.Серия + "|" + Стр2.Характеристика;
    ЦенаИзКарты = КартаЦен.Получить(КлючПоиска);
    
    Если ЦенаИзКарты <> Неопределено Тогда
        Стр2.Цена = ЦенаИзКарты;
    КонецЕсли;
КонецЦикла;

Преимущество: Поиск в Соответствии происходит практически мгновенно вне зависимости от размера таблицы. Это самый быстрый способ программного объединения «в коде».

Способ 4. Запрос по таблице значений

Многие начинающие разработчики думают, что объект Запрос предназначен только для работы с базой данных. На самом деле, мы можем передать наши ТаблицыЗначений в запрос как параметры и выполнить полноценное ЛЕВОЕ СОЕДИНЕНИЕ. Это самый надежный способ, особенно если нужно обработать ситуацию, когда в одной таблице данных больше, чем в другой.

Разберем этот метод подробнее:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЗ1.Серия КАК Серия,
    |	ТЗ1.Характеристика КАК Характеристика,
    |	ТЗ1.Цена КАК Цена
    |ПОМЕСТИТЬ ВТ_Цены
    |ИЗ
    |	&ТЗ1 КАК ТЗ1
    |;
    |
    |ВЫБРАТЬ
    |	ТЗ2.Серия КАК Серия,
    |	ТЗ2.Характеристика КАК Характеристика,
    |	ТЗ2.Количество КАК Количество
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |	&ТЗ2 КАК ТЗ2
    |;
    |
    |ВЫБРАТЬ
    |	ВТ_Данные.Серия,
    |	ВТ_Данные.Характеристика,
    |	ВТ_Данные.Количество,
    |	ЕСТЬNULL(ВТ_Цены.Цена, 0) КАК Цена
    |ИЗ
    |	ВТ_Данные КАК ВТ_Данные
    |		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Цены КАК ВТ_Цены
    |		ПО ВТ_Данные.Серия = ВТ_Цены.Серия
    |			И ВТ_Данные.Характеристика = ВТ_Цены.Характеристика";

// Передаем таблицы как параметры. Важно, чтобы колонки имели типы!
Запрос.УстановитьПараметр("ТЗ1", Таблица1);
Запрос.УстановитьПараметр("ТЗ2", Таблица2);

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

Почему это лучший выбор?

Особенности обработки отсутствующих данных

Проанализируем ситуацию, упомянутую в обсуждении: «А что если в таблице 1 больше значений, чем во второй, и наоборот?». При использовании циклов мы обычно берем одну таблицу за основу и дополняем её. Если нам нужны абсолютно все строки из обеих таблиц (аналог Full Join), нам придется:

  1. Собрать все уникальные ключи (Серия + Характеристика) из обеих таблиц в одну вспомогательную таблицу или массив.
  2. Для каждого уникального ключа создать строку в итоговой таблице.
  3. Последовательно заполнить данные из первой и второй таблиц поиском (через Соответствие или НайтиСтроки).

Рекомендации по заполнению свойств

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


НоваяСтрока = ИтоговаяТаблица.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИзТаблицы1);
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИзТаблицы2);

Этот метод автоматически сопоставит имена колонок и перенесет данные, что избавит вас от написания десятков строк присваиваний вида НоваяСтрока.Поле = СтараяСтрока.Поле.

Типизация колонок

Всегда указывайте ОписаниеТипов при программном добавлении колонок. Если колонка не будет иметь типа, это может привести к непредсказуемым ошибкам при сравнении значений (например, NULL и 0) или при передаче таблицы в запрос. Помните, что корректная типизация — залог стабильной работы алгоритмов сортировки и поиска.

Подводя итог, мы рассмотрели путь от простого перебора до использования мощных механизмов запросов и соответствий. Для оперативной работы в памяти небольших объемов данных оптимально использовать Соответствие, а для сложных структур с необходимостью объединения по разным правилам — Запросы по временным таблицам.

← На главную