Как добавить строки из одной ТаблицыЗначений в другую в 1С?

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

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

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

Метод 1: Построчное копирование с использованием ЗаполнитьЗначенияСвойств()

Один из самых гибких и часто используемых методов для копирования данных из одной строки в другую — это применение процедуры ЗаполнитьЗначенияСвойств(). Этот метод позволяет скопировать значения одноименных свойств (колонок) из исходного объекта (строки ТаблицыЗначений) в целевой.

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


// Предполагаем, что ИсходнаяТЗ уже существует и имеет колонки
// и как минимум одну строку.
// ИтоговаяТЗ - целевая таблица, в которую будем добавлять строки.
// ВАЖНО: Колонки в ИтоговойТЗ должны быть созданы заранее,
// иначе ЗаполнитьЗначенияСвойств не сможет ничего скопировать.
// Этот аспект мы подробнее рассмотрим чуть позже.

тзн1 = Новый ТаблицаЗначений;
тзн1.Колонки.Добавить("Поле1");
тзн1.Колонки.Добавить("Сумма");

тзн2 = Новый ТаблицаЗначений;
тзн2.Колонки.Добавить("Поле1");
тзн2.Колонки.Добавить("Сумма");

// Добавим несколько строк в исходную таблицу для примера
стртзн1 = тзн1.Добавить();
стртзн1.Поле1 = "Кря";
стртзн1.Сумма = 1;

стртзн1 = тзн1.Добавить();
стртзн1.Поле1 = "Ква";
стртзн1.Сумма = 2;

// Копируем строки из тзн1 в тзн2 по индексу
ЗаполнитьЗначенияСвойств(тзн2.Добавить(), тзн1[0]); // Копируем первую строку (индекс 0)
ЗаполнитьЗначенияСвойств(тзн2.Добавить(), тзн1[1]); // Копируем вторую строку (индекс 1)
ЗаполнитьЗначенияСвойств(тзн2.Добавить(), тзн1[1]); // Копируем вторую строку еще раз

// Для проверки можно вывести тзн2
// ОткрытьЗначение(тзн2);

Почему возникает ошибка "Индекс за пределами границы"?

Такая ошибка обычно возникает, когда мы пытаемся обратиться к элементу коллекции (в данном случае к строке ТаблицыЗначений) по индексу, которого не существует. Например, если ИсходнаяТЗ оказалась пустой после выполнения какого-либо запроса или фильтрации, попытка получить ИсходнаяТЗ[0] приведет к ошибке, так как строки с индексом 0 просто нет. Это очень важный момент, который часто упускают при разработке. Прежде чем обращаться к элементу по индексу, всегда следует проверять, содержит ли таблица хотя бы одну строку (Таблица.Количество() > 0).

Оптимальный подход для копирования всех строк в цикле:

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


// Предположим, ТекущийДокументТЗ - исходная ТаблицаЗначений
// ИтоговаяТЗ - целевая ТаблицаЗначений.
// Важно! ИтоговаяТЗ должна иметь аналогичные колонки,
// иначе ЗаполнитьЗначенияСвойств не сможет скопировать данные.
// Решение проблемы колонок будет рассмотрено ниже.

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

Данный код позволяет скопировать все строки из ТекущийДокументТЗ в ИтоговаяТЗ, перенося значения из одноименных колонок.

Важный момент: Инициализация колонок целевой ТаблицыЗначений

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

Решение: используйте метод СкопироватьКолонки()

Чтобы целевая ТаблицаЗначений имела такую же структуру колонок, как и исходная, мы должны сначала скопировать колонки. Для этого идеально подходит метод СкопироватьКолонки(). Этот метод создает новую ТаблицуЗначений, которая полностью повторяет структуру колонок исходной таблицы, но при этом она остается пустой (не содержит строк).


ИтоговаяТЗ = ТекущийДокументТЗ.СкопироватьКолонки(); // Теперь ИтоговаяТЗ имеет те же колонки, что и ТекущийДокументТЗ

Полный пример использования ЗаполнитьЗначенияСвойств() с правильной инициализацией:

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


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

Стр = ИсходнаяТЗ.Добавить();
Стр.Фрукт = "Яблоко";
Стр.Цвет = "Зелёный";
Стр.Вес = 200;

Стр = ИсходнаяТЗ.Добавить();
Стр.Фрукт = "Банан";
Стр.Цвет = "Желтый";
Стр.Вес = 150;

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


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

// Теперь ИтоговаяТЗ содержит все строки из ИсходнаяТЗ с той же структурой колонок
// Для проверки можно вывести ИтоговаяТЗ
// ОткрытьЗначение(ИтоговаяТЗ);

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

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

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

Метод 2: Использование методов Выгрузить() и Загрузить()

Наиболее эффективным способом полного копирования всех строк и колонок из одной ТаблицыЗначений в другую является использование связки методов Выгрузить() и Загрузить(). Эти методы предназначены для работы с данными в виде таблиц и обеспечивают высокую производительность.

  1. Метод Выгрузить(), вызванный без параметров, возвращает новую ТаблицуЗначений, содержащую все данные (колонки и строки) исходной таблицы. Если вызвать ИсходнаяТЗ.Выгрузить(ЦелеваяТЗ), то данные из ИсходнаяТЗ будут скопированы в уже существующую ЦелеваяТЗ, при этом ЦелеваяТЗ должна иметь совместимые колонки.
  2. Метод Загрузить() загружает данные в существующую ТаблицуЗначений из другого источника, например, из результата Выгрузить() или из другой ТаблицыЗначений.

Пример полного копирования:


// Предположим, ИсходнаяТЗ уже заполнена данными
// (как в примере выше с "Яблоком" и "Бананом").

// Вариант 1: Копирование всех данных (структуры и строк) в новую ТаблицуЗначений.
// Это самый простой и часто рекомендуемый способ для полного копирования.
ИтоговаяТЗ_Вариант1 = ИсходнаяТЗ.Выгрузить();
// ИтоговаяТЗ_Вариант1 теперь является полной копией ИсходнаяТЗ,
// включая структуру колонок и все строки.

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

// Загружаем данные из ИсходнаяТЗ в ИтоговаяТЗ_Вариант2.
// Если ИтоговаяТЗ_Вариант2 была абсолютно пустой,
// колонки будут созданы по образцу ИсходнаяТЗ.
// Если колонки уже были, они должны совпадать по имени и типу,
// иначе возможны ошибки или неполное копирование.
ИтоговаяТЗ_Вариант2.Загрузить(ИсходнаяТЗ);

// Или другой способ, который также скопирует строки в существующую таблицу
// (при условии, что ИтоговаяТЗ_Вариант3 уже имеет нужные колонки,
// или создается как пустая и структура колонок возьмется из источника):
// ИтоговаяТЗ_Вариант3 = Новый ТаблицаЗначений;
// ИсходнаяТЗ.Выгрузить(ИтоговаяТЗ_Вариант3);

Использование Выгрузить() без параметров или с параметром ТаблицаЗначений (как в ИсходнаяТЗ.Выгрузить(ИтоговаяТЗ)) позволяет гибко управлять копированием. Если целевая таблица уже имеет колонки, ИтоговаяТЗ.Загрузить(ИсходнаяТЗ) или ИсходнаяТЗ.Выгрузить(ИтоговаяТЗ) будет работать очень эффективно.

Метод 3: Добавление одной строки с ЗаполнитьЗначенияСвойств() или Добавить(ИсходнаяСтрока)

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


// Предположим, ИсходнаяТЗ и ИтоговаяТЗ уже существуют и имеют схожую структуру колонок
// Если ИтоговаяТЗ новая, сначала скопируйте колонки:
// ИтоговаяТЗ = ИсходнаяТЗ.СкопироватьКолонки();

// Добавление первой строки ИсходнойТЗ в ИтоговуюТЗ:
// Вариант с ЗаполнитьЗначенияСвойств()
Если ИсходнаяТЗ.Количество() > 0 Тогда
    ЗаполнитьЗначенияСвойств(ИтоговаяТЗ.Добавить(), ИсходнаяТЗ[0]);
Иначе
    Сообщить("Исходная таблица пуста, невозможно скопировать строку по индексу.");
КонецЕсли;

// Вариант с Добавить(ИсходнаяСтрока) - более лаконичен и удобен.
// Этот метод создает новую строку в ИтоговойТЗ и заполняет ее значениями из ИсходнаяТЗ[0].
// Колонкам в ИтоговойТЗ должна быть уже задана соответствующая структура.
Если ИсходнаяТЗ.Количество() > 0 Тогда
    ИтоговаяТЗ.Добавить(ИсходнаяТЗ[0]);
Иначе
    Сообщить("Исходная таблица пуста, невозможно добавить строку.");
КонецЕсли;

Метод Добавить(ИсходнаяСтрока) является более современным и удобным для копирования одной строки, так как он создает новую строку и сразу же заполняет ее данными из указанной строки-источника. Это более лаконичная альтернатива для ЗаполнитьЗначенияСвойств() в данном контексте.

Метод 4: Использование запросов 1С

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


Запрос = Новый Запрос;
Запрос.Текст = "
    |ВЫБРАТЬ
    |    ИсходнаяТаблица.Фрукт КАК НаименованиеФрукта,
    |    ИсходнаяТаблица.Сумма * 1.2 КАК ОбновленнаяСуммаВеса
    |ИЗ
    |    &ИсходнаяТЗ КАК ИсходнаяТаблица
    |ГДЕ
    |    ИсходнаяТаблица.Вес > 100
    |";
Запрос.УстановитьПараметр("ИсходнаяТЗ", ИсходнаяТЗ); // Передаем нашу ТаблицуЗначений как параметр запроса

ИтоговаяТЗ_ИзЗапроса = Запрос.Выполнить().Выгрузить();
// Теперь ИтоговаяТЗ_ИзЗапроса содержит отфильтрованные и преобразованные данные

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

Производительность различных методов

При работе с большими объемами данных производительность может стать критически важной. Мы можем выделить следующие общие рекомендации:

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

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

Предотвращение ошибок "Индекс за пределами границы"

Как мы уже выяснили, одной из частых причин ошибок при работе с ТаблицейЗначений является попытка обратиться по индексу к несуществующей строке (например, Таблица[0] в пустой таблице). Чтобы избежать этого, всегда рекомендуется перед обращением по индексу проверять количество строк в таблице:


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

Или, еще лучше, использовать цикл Для каждого Строка Из Таблица Цикл ... КонецЦикла, который автоматически обходит только существующие строки и не вызовет ошибку при пустой таблице. Этот подход является более надежным и предотвращает ошибки в большинстве случаев.

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

← На главную