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

· 9 мин

Содержание

Скопировать колонку таблицы значений в 1С можно четырьмя способами. ВыгрузитьКолонку() быстро вынимает значения в массив. Скопировать() создаёт новую таблицу только с нужными колонками. Цикл по строкам даёт полный контроль над значениями. Запрос к таблице значений позволяет попутно отфильтровать и свернуть данные.

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

Метода с названием СкопироватьКолонку у таблицы значений нет. Его часто ищут в справке и не находят. Есть СкопироватьКолонки() (во множественном числе), но он копирует только структуру, без данных. Разберём, что за чем стоит.

Какой способ выбрать

СпособЧто получаетеКогда братьОграничение
ВыгрузитьКолонкуМассив значений одной колонкиНужен список без структуры таблицыТолько одна колонка за вызов
СкопироватьНовая таблица с выбранными колонками и строкамиНужна «урезанная» копия таблицыКолонки перечисляются именами
Цикл по строкамЧто угодно: колонка в другой таблице, массив, соответствиеЗначения нужно менять при копированииСамый медленный на больших объёмах
Запрос к ТЗТаблица после отбора, группировки, соединенийКопирование вместе с агрегациейСложнее код, нужны типы колонок

Выгрузка колонки в массив и загрузка обратно

Начните с этого метода. Он решает задачу «взять все значения одной колонки» за один вызов.

Массив = ТЗ.ВыгрузитьКолонку("Номенклатура");

В массиве будет столько элементов, сколько строк в таблице, в том же порядке. Колонку можно указать по имени, по индексу или объектом ТЗ.Колонки.Номенклатура.

Обратная операция называется ЗагрузитьКолонку. Она кладёт значения массива в колонку, строку за строкой:

ТЗ2.ЗагрузитьКолонку(Массив, "Номенклатура");

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

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

Для Каждого Элемент Из Массив Цикл
	ТЗ2.Добавить();
КонецЦикла;

ТЗ2.ЗагрузитьКолонку(Массив, "Номенклатура");

Если нужны уникальные значения, не перебирайте массив руками. Выгрузите колонку в массив и переложите его в Соответствие, где ключ — значение.

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

Уникальные = ТЗ.Скопировать(, "Номенклатура");
Уникальные.Свернуть("Номенклатура");
Массив = Уникальные.ВыгрузитьКолонку("Номенклатура");

Исходная таблица при этом не меняется, а в массиве остаются только неповторяющиеся значения.

Скопировать таблицу целиком или только нужные колонки

Метод Скопировать возвращает новую таблицу значений. Первый параметр задаёт строки (можно пропустить или передать структуру отбора), второй — список колонок через запятую.

// Только две колонки, все строки
НоваяТЗ = ТЗ.Скопировать(, "Номенклатура, Количество");

// Только строки по отбору
Отбор = Новый Структура("Склад", Склад);
НоваяТЗ = ТЗ.Скопировать(Отбор, "Номенклатура, Количество");

Изменения в новой таблице не затрагивают исходную. Это независимая копия, строки в ней — новые объекты.

Если нужна пустая заготовка с теми же колонками и типами, вызывайте СкопироватьКолонки():

Заготовка = ТЗ.СкопироватьКолонки();

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

Одна колонка в новую таблицу

Нужна таблица из одной колонки? Передайте её имя вторым параметром:

ТолькоНоменклатура = ТЗ.Скопировать(, "Номенклатура");

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

Копирование колонки циклом по строкам

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

Допустим, в таблице ТЗ есть колонка Цена, а в ТЗ2 нужна колонка ЦенаСНДС, где значение умножено на 1,2.

ТЗ2 = ТЗ.Скопировать();
ТЗ2.Колонки.Добавить("ЦенаСНДС", Новый ОписаниеТипов("Число",,, Новый КвалификаторыЧисла(15, 2)));

Для Каждого Строка Из ТЗ2 Цикл
	Строка.ЦенаСНДС = Окр(Строка.Цена * 1.2, 2);
КонецЦикла;

Проверим арифметику: при цене 100 получим 120,00, при цене 99,99 — 119,99 (99,99 × 1,2 = 119,988, округляем до 119,99).

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

Для Индекс = 0 По ТЗ.Количество() - 1 Цикл
	ТЗ2[Индекс].Результат = ТЗ[Индекс].Источник;
КонецЦикла;

Индексы строк таблицы значений начинаются с нуля. Ошибка на единицу при границе цикла — одна из самых частых.

Не добавляйте и не удаляйте строки таблицы внутри Для Каждого по ней же. Если нужно удалить строки, собирайте их в массив и удаляйте после цикла, либо идите обратным циклом по индексам.

Копирование через запрос к таблице значений

В запросе таблицу значений можно использовать как источник. Её передают параметром и обращаются к ней в блоке ИЗ:

Запрос = Новый Запрос;
Запрос.Текст =
	"ВЫБРАТЬ
	|	Т.Номенклатура КАК Номенклатура,
	|	СУММА(Т.Количество) КАК Количество
	|ИЗ
	|	&ТЗ КАК Т
	|СГРУППИРОВАТЬ ПО
	|	Т.Номенклатура";
Запрос.УстановитьПараметр("ТЗ", ТЗ);
Результат = Запрос.Выполнить().Выгрузить();

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

Условие: у колонок исходной таблицы должны быть заданы типы. Если колонка создана без типа (ТЗ.Колонки.Добавить("Количество")), запрос может отказаться работать с ней или вернуть неожиданные результаты. Поэтому колонки, которые пойдут в запрос, всегда создавайте с описанием типов.

Для переноса в запрос уместно знать и такой вариант: поместить таблицу значений во временную таблицу через МенеджерВременныхТаблиц и обращаться к ней по имени. Это нужно, если одна и та же таблица используется в нескольких запросах пакета.

Копирование колонки с условием

Часто нужны не все значения, а только подходящие. Вариантов два.

Первый: отобрать строки методом НайтиСтроки, затем пройти по найденному массиву.

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

Второй: сначала Скопировать(Отбор, "Номенклатура"), затем ВыгрузитьКолонку. Он короче и не требует цикла, поэтому на больших таблицах работает быстрее.

Отбор по структуре сравнивает значения на равенство. Условия «больше», «меньше» или «содержит» так не задать. Для них пишите цикл с проверкой либо переходите на запрос.

Типы данных и составные типы

Когда колонка создаётся без типа, в неё можно положить что угодно. Это удобно в черновике, но осложняет копирование. После Скопировать и СкопироватьКолонки типы колонок сохраняются. А при ручном создании колонки в приёмнике вы задаёте их сами.

Типы = Новый ОписаниеТипов("Число, Строка");
ТЗ2.Колонки.Добавить("Значение", Типы);

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

Чтобы привести значение явно, пользуйтесь методом описания типов:

Строка.Значение = Типы.ПривестиЗначение(ИсходноеЗначение);

Что стоит проверить перед копированием колонки между таблицами:

  • 🔹 типы совпадают или тип приёмника шире типа источника;
  • 🔹 у строк и чисел задана длина и точность: строка без ограничения длины и строка длиной 10 поведут себя по-разному;
  • 🔹 ссылочные типы приёмника включают все нужные справочники и документы;
  • 🔹 состав даты: если в приёмнике колонка с типом «Дата» и составом «Дата», время отбросится.

Не уверены в типе источника? Посмотрите ТЗ.Колонки.Номенклатура.ТипЗначения. Это объект ОписаниеТипов, из него можно получить список типов и квалификаторы.

Скорость на больших таблицах

Для тысяч строк разница между способами заметна, для сотен тысяч — критична. Общий порядок такой: быстрее всех встроенные методы (ВыгрузитьКолонку, Скопировать, Свернуть), затем запрос, медленнее всех цикл с обращением к полям по именам.

Что ускоряет работу:

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

Вложенный цикл «для каждой строки одной таблицы — искать в другой» на 50 000 × 50 000 строк даёт 2,5 миллиарда сравнений. Такой код висит минутами. Замените его на Соответствие по ключу или на запрос с соединением.

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

Копирование между строками и в табличную часть

Иногда колонку нужно «скопировать» внутри одной таблицы: например, заполнить ЦенаСклада значениями из Цена. Здесь подойдёт цикл, но есть и короткий путь через массив:

ТЗ.ЗагрузитьКолонку(ТЗ.ВыгрузитьКолонку("Цена"), "ЦенаСклада");

Две колонки должны быть в таблице заранее. Значения переедут как есть, с приведением к типу приёмника.

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

  1. Выгрузить табличную часть в таблицу значений.
  2. Добавить или преобразовать нужную колонку (цикл, ЗагрузитьКолонку).
  3. Загрузить таблицу обратно.

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

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

Ошибки при копировании колонок и как их лечить

Основные сообщения, которые встречаются при работе с колонками:

  • Поле объекта не обнаружено (Номенклатура): в таблице нет колонки с таким именем. Регистр букв значения не имеет, а вот опечатка или обращение к колонке другой таблицы — частая причина.
  • Колонка с таким именем уже существует: вы вызываете Колонки.Добавить повторно. Перед добавлением проверьте ТЗ.Колонки.Найти("Имя") = Неопределено.
  • Индекс за пределами допустимого диапазона: цикл по индексам вышел за размер таблицы. Граница верхняя — Количество() - 1.
  • Пустые значения после копирования. Колонка приёмника имеет другой тип, и платформа молча заменила значения значением по умолчанию.

Для отладки остановитесь точкой останова после копирования и откройте таблицу в отладчике. Сравните число строк, типы колонок и первые 2-3 значения. Обычно проблема видна сразу.

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

Попытка
	Массив = ТЗ.ВыгрузитьКолонку("Номенклатура");
Исключение
	ЗаписьЖурналаРегистрации("КопированиеКолонки",
		УровеньЖурналаРегистрации.Ошибка,,,
		ОписаниеОшибки());
	ВызватьИсключение;
КонецПопытки;

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

Перед копированием колонки
Выполнено: 0 / 4

Частые вопросы

Как скопировать несколько колонок таблицы значений?
Используйте ТЗ.Скопировать(, "Колонка1, Колонка2"). Получится новая таблица только с этими колонками и всеми строками.
Чем `СкопироватьКолонки` отличается от `Скопировать`?
СкопироватьКолонки() создаёт пустую таблицу со структурой исходной. Скопировать() переносит ещё и строки с данными.
Можно ли переименовать колонку при копировании?
Метод Скопировать оставляет прежние имена. Переименовать можно через запрос (Т.Цена КАК ЦенаЗакупки) или создав в приёмнике колонку с новым именем и перенеся значения через ВыгрузитьКолонку/ЗагрузитьКолонку.
Влияет ли копия на исходную таблицу?
Нет. Скопировать и ВыгрузитьКолонку возвращают независимые данные. Если же в ячейках лежат ссылки на объекты (например, структуры), они остаются общими.
Как скопировать колонку в табличную часть документа?
Табличная часть умеет Загрузить из таблицы значений и Выгрузить в неё. Колонки сопоставляются по именам, лишние пропускаются.