Перенос строк и колонок между таблицами значений: пять способов с кодом
Содержание
Чтобы скопировать таблицу значений в другую таблицу значений в 1С 8.3, вызовите ТЗКопия = ТЗ.Скопировать(); — метод вернёт новую независимую таблицу с теми же колонками и строками. Нужны не все строки или не все колонки — передайте в Скопировать() массив строк или структуру отбора и список колонок через запятую. Нужна только пустая структура — используйте СкопироватьКолонки().
Частая ловушка: ТЗ2 = ТЗ1 ничего не копирует. Обе переменные смотрят на один и тот же объект, и правка в ТЗ2 тут же «появляется» в ТЗ1. Ниже — все рабочие способы переноса данных, с кодом, который можно вставить в модуль и запустить.
Как устроена таблица значений и что именно копируется
Таблица значений — универсальная коллекция, которая живёт только в памяти. Она доступна на сервере и в толстом клиенте. В тонком клиенте и веб-клиенте её нет: на управляемой форме вместо неё работает реквизит формы типа ТаблицаЗначений, а на клиенте вы видите ДанныеФормыКоллекция.
У таблицы три части, которые важно различать при копировании:
- 📐 Колонки (
ТЗ.Колонки) — имя, тип значения (ОписаниеТипов), заголовок, ширина. - 📄 Строки — элементы типа
СтрокаТаблицыЗначений, к ним обращаются по индексуТЗ[0]или в циклеДля Каждого. - 🔎 Индексы (
ТЗ.Индексы) — ускоряютНайти()иНайтиСтроки().
Присваивание и копия — не одно и то же
ТЗ1 = Новый ТаблицаЗначений;
ТЗ1.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
ТЗ1.Добавить().Сумма = 100;
ТЗ2 = ТЗ1; // ссылка на тот же объект
ТЗ2[0].Сумма = 500;
Сообщить(ТЗ1[0].Сумма); // 500 — изменился «оригинал»
ТЗ3 = ТЗ1.Скопировать(); // отдельный объект
ТЗ3[0].Сумма = 1;
Сообщить(ТЗ1[0].Сумма); // по-прежнему 500
Копия неглубокая. Ссылки на справочники и документы копируются как ссылки — это нормально, сам элемент справочника не дублируется. Но если в ячейке лежит другая коллекция (массив, вложенная таблица, структура), обе таблицы будут указывать на один и тот же вложенный объект. Его изменение отразится в обеих.
Метод Скопировать(): полная копия и копия по отбору
Скопировать(Строки, Колонки) — самый быстрый и короткий способ скопировать таблицу значений в другую. Он выполняется внутри платформы, без цикла на встроенном языке.
Оба параметра необязательные:
| Параметр | Что передать | Результат |
|---|---|---|
| не указан | — | все строки и все колонки |
Строки | Массив строк исходной таблицы | только эти строки |
Строки | Структура отбора | строки, где значения колонок равны значениям структуры |
Колонки | строка "Имя1, Имя2" | только перечисленные колонки |
Полная копия
ТЗКопия = ТЗИсточник.Скопировать();
Копия с отбором по значению
Допустим, в таблице остатков 3 000 строк, а нужны только строки по одному складу и всего две колонки:
Отбор = Новый Структура("Склад", ВыбранныйСклад);
ТЗСклад = ТЗОстатки.Скопировать(Отбор, "Номенклатура, Количество");
Копия выбранных строк
Если условие сложнее равенства (например, «количество больше нуля»), сначала соберите массив строк, а потом передайте его:
МассивСтрок = Новый Массив;
Для Каждого Стр Из ТЗОстатки Цикл
Если Стр.Количество > 0 Тогда
МассивСтрок.Добавить(Стр);
КонецЕсли;
КонецЦикла;
ТЗПоложительные = ТЗОстатки.Скопировать(МассивСтрок);
Массив для простого равенства удобно получить через НайтиСтроки(): ТЗ.Скопировать(ТЗ.НайтиСтроки(Отбор)) даёт тот же результат, что и передача структуры.
Скопировать() всегда создаёт новую таблицу. Если приёмник уже существует и в нём есть данные, которые нужно сохранить, этот метод не подходит — используйте построчный перенос из раздела ниже.
СкопироватьКолонки, ВыгрузитьКолонку и ЗагрузитьКолонку
Эти методы нужны, когда переносите не всю таблицу, а структуру или отдельные колонки.
Пустая таблица с той же структурой
СкопироватьКолонки() возвращает таблицу без строк, но с теми же колонками, типами и заголовками. Можно передать имена нужных колонок строкой:
ТЗПриемник = ТЗИсточник.СкопироватьКолонки();
// или только часть колонок
ТЗПриемник = ТЗИсточник.СкопироватьКолонки("Номенклатура, Цена");
Это правильная заготовка под накопление строк из нескольких источников: колонки уже типизированы, и такую таблицу потом можно передать в запрос.
Перенос одной колонки через массив
ВыгрузитьКолонку() отдаёт значения колонки массивом в порядке строк. ЗагрузитьКолонку() раскладывает массив по строкам приёмника.
// В приёмнике уже есть строки, добавляем к ним колонку цен
ТЗПриемник.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число",
Новый КвалификаторыЧисла(15, 2)));
Цены = ТЗИсточник.ВыгрузитьКолонку("Цена");
ТЗПриемник.ЗагрузитьКолонку(Цены, "Цена");
ЗагрузитьКолонку() не добавляет строки: она заполняет только те, что уже есть, по порядку. Значит, строки в двух таблицах должны идти в одном порядке и соответствовать друг другу. Если источник отсортирован иначе, цена «уедет» на чужую номенклатуру, и ошибки при этом не будет.
Как подготовить приёмник под загрузку колонки:
- Создайте структуру:
ТЗПриемник = ТЗИсточник.СкопироватьКолонки("Номенклатура"). - Добавьте нужное число пустых строк циклом
Для Сч = 1 По ТЗИсточник.Количество() Цикл ТЗПриемник.Добавить(); КонецЦикла;. - Загрузите каждую колонку:
ТЗПриемник.ЗагрузитьКолонку(ТЗИсточник.ВыгрузитьКолонку("Номенклатура"), "Номенклатура").
На практике этот путь выбирают редко: для полной копии хватает Скопировать(). Зато ВыгрузитьКолонку() незаменим, когда из таблицы нужен просто список значений — например, массив ссылок для параметра запроса В (&Список).
Копирование таблицы значений через запрос и конструктор запроса
Запрос нужен, когда при переносе данные надо сгруппировать, соединить с базой или отфильтровать сложным условием. Таблицу значений передают параметром и сначала помещают во временную таблицу — выбирать напрямую из параметра можно только в запросе с ПОМЕСТИТЬ.
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ТЗ", ТЗИсточник);
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ТЗ КАК Т
|;
|////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ.Номенклатура КАК Номенклатура,
| СУММА(ВТ.Количество) КАК Количество
|ИЗ
| ВТ_Данные КАК ВТ
|ГДЕ
| ВТ.Количество > 0
|СГРУППИРОВАТЬ ПО
| ВТ.Номенклатура";
ТЗРезультат = Запрос.Выполнить().Выгрузить();
Выгрузить() результата запроса сразу возвращает новую таблицу значений — отдельный цикл не нужен.
Обязательное условие: типизированные колонки
Если колонку создали без типа (ТЗ.Колонки.Добавить("Сумма")), запрос упадёт с ошибкой Тип не может быть выбран в запросе. Каждой колонке, которая попадает в запрос, задайте ОписаниеТипов:
ТЗ.Колонки.Добавить("Номенклатура",
Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ.Колонки.Добавить("Количество",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Таблица, полученная из Выгрузить() другого запроса или из ТабличнаяЧасть.Выгрузить(), уже типизирована — с ней проблем нет.
Как собрать такой запрос в конструкторе
- В модуле в Конфигураторе установите курсор внутри строки текста запроса и вызовите конструктор из контекстного меню (
Конструктор запроса...). - На вкладке с таблицами создайте описание временной таблицы: задайте имя, например
ВТ_Данные, и перечислите поля с типами — те же, что у колонок вашей таблицы. - В тексте, который сформирует конструктор, замените источник на параметр
&ТЗи добавьтеПОМЕСТИТЬ. - Добавьте второй запрос пакета, где из временной таблицы выбираете, группируете и фильтруете.
Временная таблица живёт до конца выполнения запроса. Если она нужна в нескольких разных запросах, создайте Новый МенеджерВременныхТаблиц и присвойте его свойству Запрос.МенеджерВременныхТаблиц каждого из них.
Объединение двух таблиц в одну
Склеить две таблицы с одинаковыми колонками удобно тем же приёмом: обе помещаются во временные таблицы, а итог собирается через ОБЪЕДИНИТЬ ВСЕ. Встроенного метода «добавить все строки другой таблицы» у таблицы значений нет.
Построчный перенос: цикл, ЗаполнитьЗначенияСвойств и выборка
Цикл нужен, когда приёмник уже существует и в нём есть данные, когда колонки называются по-разному или когда значения надо пересчитать по дороге.
ЗаполнитьЗначенияСвойств — перенос одноимённых колонок
Для Каждого СтрИсточник Из ТЗИсточник Цикл
НоваяСтрока = ТЗПриемник.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрИсточник);
КонецЦикла;
Процедура копирует значения только тех колонок, имена которых совпадают в обеих таблицах. Лишние колонки источника игнорируются, недостающие в приёмнике остаются пустыми. Третий и четвёртый параметры ограничивают перенос: ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрИсточник, "Номенклатура, Цена") скопирует только две колонки, а параметр «исключая свойства» — все, кроме перечисленных.
Разные имена колонок и пересчёт
Для Каждого СтрИсточник Из ТЗИсточник Цикл
НоваяСтрока = ТЗПриемник.Добавить();
НоваяСтрока.Товар = СтрИсточник.Номенклатура;
НоваяСтрока.Сумма = СтрИсточник.Количество * СтрИсточник.Цена;
КонецЦикла;
Проверим пример на числах: при количестве 4 и цене 125,50 в колонку Сумма попадёт 502.
Перенос из выборки результата запроса
Если данные приходят из запроса, а приёмник уже заполнен, выгружать результат в промежуточную таблицу необязательно — достаточно обойти выборку:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = ТЗПриемник.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
КонецЦикла;
Здесь ЗаполнитьЗначенияСвойств берёт значения полей выборки по псевдонимам из запроса. Поэтому псевдонимы КАК ... должны совпадать с именами колонок приёмника.
Табличная часть документа и реквизит формы
Табличная часть объекта — не таблица значений, но конвертируется в неё в одну строку:
ТЗ = ДокументОбъект.Товары.Выгрузить();— получить таблицу значений из табличной части;ДокументОбъект.Товары.Загрузить(ТЗ);— полностью заменить строки табличной части данными таблицы (одноимённые колонки).
На управляемой форме код работы с таблицей значений пишут в серверной процедуре (&НаСервере): ТЗ = РеквизитФормыВЗначение("ИмяРеквизита"), обработка, затем ЗначениеВРеквизитФормы(ТЗ, "ИмяРеквизита").
Какой метод копирования выбрать: сравнение
| Способ | Когда подходит | Приёмник | Риски |
|---|---|---|---|
Скопировать() | полная копия или копия по простому отбору | создаётся новый | неглубокая копия вложенных коллекций |
СкопироватьКолонки() | нужна только структура | создаётся новый, пустой | строки не переносятся |
ВыгрузитьКолонку() + ЗагрузитьКолонку() | перенос одной колонки в готовые строки | существующий | сдвиг значений при разном порядке строк |
Запрос с ПОМЕСТИТЬ | группировка, соединение с базой, сложный отбор | создаётся новый (Выгрузить()) | нетипизированные колонки, только на сервере |
Цикл + ЗаполнитьЗначенияСвойств | дописать в существующую таблицу, разные имена, пересчёт | существующий | медленнее на больших объёмах |
ТЗ.Скопировать() или ТЗ.Скопировать(Новый Структура(...), "Колонки").ЗаполнитьЗначенияСвойств(ТЗПриемник.Добавить(), Стр).&ТЗ → ПОМЕСТИТЬ → второй запрос пакета → Выгрузить().ТЗ.ВыгрузитьКолонку("Имя") — вернёт массив.Если задача — просто сгруппировать строки и сложить суммы, запрос не обязателен. Метод ТЗКопия.Свернуть("Номенклатура", "Количество, Сумма") сворачивает таблицу на месте. Чтобы не испортить исходник, сначала сделайте Скопировать(), а сворачивайте копию.
Ошибки при копировании таблиц значений и как ускорить перенос
Частые ошибки
- 🔗 Присваивание вместо копии.
ТЗ2 = ТЗ1— одна таблица под двумя именами. Симптом: «сами собой» меняются строки в исходных данных. - 🧩 Нетипизированная колонка в запросе — ошибка
Тип не может быть выбран в запросе. Лечится заданиемОписаниеТиповприКолонки.Добавить(). - 🏷 Не совпадают имена колонок.
ЗаполнитьЗначенияСвойствмолча пропускает колонку с другим именем, а в приёмнике остаётся пустое значение. Сверьте имена или присвойте значение явно. - 🖥 Таблица значений в клиентском коде. На тонком клиенте объекта нет, будет ошибка о недоступности типа. Переносите обработку в процедуру
&НаСервере. - 🗑 Удаление строк в прямом цикле.
Для КаждогосТЗ.Удалить()пропускает строки. Удаляйте с конца:
Для Сч = 1 По ТЗ.Количество() Цикл
Стр = ТЗ[ТЗ.Количество() - Сч];
Если Стр.Количество = 0 Тогда
ТЗ.Удалить(Стр);
КонецЕсли;
КонецЦикла;
Граница цикла вычисляется один раз при входе, а индекс считается от текущего размера. Для таблицы из 5 строк на первом шаге берётся ТЗ[4]; если её удалили, на втором шаге ТЗ[3] — последняя из оставшихся. Так каждая строка проверяется ровно один раз.
Как ускорить перенос больших таблиц
- ⚡ Предпочитайте платформенные методы циклам.
Скопировать()иВыгрузить()результата запроса работают внутри платформы и на десятках тысяч строк заметно быстрее, чем цикл сДобавить(). - 🔎 Добавьте индекс перед поиском. Если в цикле переноса вы ищете соответствие во второй таблице через
НайтиСтроки(), сначала выполнитеТЗ.Индексы.Добавить("Номенклатура"). Без индекса каждый поиск перебирает всю таблицу. - 📦 Копируйте только нужные колонки. Параметр
КолонкивСкопировать()уменьшает объём памяти, особенно когда в источнике есть строковые колонки неограниченной длины. - 🧮 Не гоняйте таблицу между клиентом и сервером. Каждый серверный вызов с большим реквизитом формы — передача данных по сети. Собирайте и обрабатывайте всё за один вызов
&НаСервере. - 🧹 Освобождайте память. После работы с крупной временной таблицей присвойте переменной
Неопределено, если процедура продолжает работать дальше.
Частые вопросы
Копирует ли Скопировать() индексы исходной таблицы?
ТЗКопия.Индексы.Добавить("Колонка").Можно ли скопировать таблицу значений в дерево значений?
Дерево.Строки.Добавить() и ЗаполнитьЗначенияСвойств.Как передать таблицу значений с сервера на клиент?
ТаблицаЗначений и ЗначениеВРеквизитФормы, либо поместите таблицу во временное хранилище через ПоместитьВоВременноеХранилище и передайте на клиент адрес.Как отсортировать копию, не меняя оригинал?
ТЗКопия = ТЗ.Скопировать(), затем ТЗКопия.Сортировать("Дата Убыв, Номер"). Сортировать() работает на месте и меняет ту таблицу, у которой вызван.