Пустая копия ТЗ: колонки, типы, квалификаторы и данные

· 9 мин

Содержание

Чтобы скопировать структуру таблицы значений в 1С без строк, вызовите НоваяТЗ = ТЗ.СкопироватьКолонки();. Метод создаёт новую таблицу с теми же колонками: имена, типы значений, заголовки и ширина переносятся, строки — нет.

Частая ошибка новичка — строка НоваяТЗ = ТЗ;. Она ничего не копирует. Обе переменные смотрят на один и тот же объект, и любое НоваяТЗ.Очистить() стирает исходные данные. Для копии всегда нужен метод, который создаёт новый объект: СкопироватьКолонки() или Скопировать().

Когда нужна копия колонок таблицы значений

Пустая таблица с той же структурой нужна чаще, чем кажется. Типичные случаи из практики:

  • 📥 Накопитель результата. Вы обходите исходную таблицу, отбираете строки по сложному условию и складываете их в новую таблицу той же формы.
  • 🧮 Сравнение «было — стало». Перед изменением данных снимаете копию, после — сравниваете две таблицы построчно.
  • 🔀 Разделение на части. Одна выгрузка из запроса делится на несколько таблиц: по складам, по организациям, по периодам.
  • 📤 Передача в другую процедуру. Вызываемый код должен получить «бланк» с правильными колонками и заполнить его сам.
  • 🧪 Тесты и обработки. Нужна пустая таблица того же вида, что вернул запрос, чтобы проверить алгоритм на подготовленных строках.

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

Метод СкопироватьКолонки: структура без строк

Это штатный способ, и в 9 случаях из 10 его достаточно.

// Полная копия структуры
НоваяТЗ = ТЗ.СкопироватьКолонки();

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

Что переносится в новую таблицу:

Свойство колонкиПереносится
Имяда
Тип значения (описание типов с квалификаторами)да
Заголовокда
Ширинада
Строки с данныминет
Индексынет, создавайте заново

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

💡 Если нужна структура почти целиком, кроме одной-двух колонок, проще скопировать всё и удалить лишнее:

НоваяТЗ = ТЗ.СкопироватьКолонки();
НоваяТЗ.Колонки.Удалить("СлужебноеПоле");

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

НоваяТЗ.Индексы.Добавить("Номенклатура");

Перенос структуры через цикл по коллекции Колонки

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

Базовый вариант — точная копия, собранная руками:

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

Метод Колонки.Добавить() принимает четыре параметра: имя, описание типов, заголовок и ширину. Все, кроме имени, необязательные. Если опустить тип, колонка примет значения любого типа.

Пример: объединить структуру двух таблиц

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

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

Проверка через Колонки.Найти() обязательна. Повторное добавление колонки с уже существующим именем вызывает ошибку времени выполнения.

Пример: список имён колонок строкой

Строку с именами удобно получить один раз и передать в СкопироватьКолонки(), Скопировать() или ЗаполнитьЗначенияСвойств():

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

Функции СтрРазделить() и СтрСоединить() доступны в платформе 8.3 начиная с релиза 8.3.6. На более старых релизах собирайте строку конкатенацией в цикле.

Пример: проверить, что структуры двух таблиц совпадают

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

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

Описания типов сравниваются через сериализацию: так в сравнение попадают и квалификаторы, а код выполняется на сервере. Функция не учитывает порядок колонок: для ЗаполнитьЗначенияСвойств() он не важен. Если вам нужен ещё и одинаковый порядок, сравнивайте колонки по индексу, а не по имени.

Копия таблицы значений вместе с данными

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

// Полная копия: все строки, все колонки
Копия = ТЗ.Скопировать();

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

// Конкретные строки — массивом
Строки = ТЗ.НайтиСтроки(Новый Структура("Отгружено", Ложь));
Неотгруженные = ТЗ.Скопировать(Строки);

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

Перенос строк в уже готовую пустую таблицу

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

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

Метод копирует значения колонок с совпадающими именами. Колонки, которых нет в приёмнике, молча пропускаются. Это удобно, но и коварно: опечатку в имени колонки вы не заметите, просто получите пустое поле.

Глубокая копия через сериализацию

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

Для полной независимости применяют сериализацию:

ГлубокаяКопия = ЗначениеИзСтрокиВнутр(ЗначениеВСтрокуВнутр(ТЗ));

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

Типы данных и квалификаторы колонок

Тип колонки задаётся объектом ОписаниеТипов. Вместе с типом в нём хранятся квалификаторы: длина строки, разрядность и точность числа, состав даты.

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

Квалификаторы не формальность. Колонка с типом и квалификаторами приводит значение при записи:

Тип колонкиЗаписалиХранится
Число (15, 2)1234.5671234.57
Строка (5)“Москва”“Москв”
Дата (только дата)07.10.2026 14:35:0007.10.2026 00:00:00
Без типалюбоекак есть

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

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

Свойство ТипЗначения у существующей колонки только для чтения. Новый тип задают при создании колонки. В цикле переноса структуры это делается просто:

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

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

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

Тип Null после выгрузки запроса

Таблица из Запрос.Выполнить().Выгрузить() часто содержит в типе колонки Null. Он появляется на полях из левого соединения. Копия структуры унаследует этот тип. Чтобы убрать его:

ТипБезNull = Новый ОписаниеТипов(Колонка.ТипЗначения, , "Null");

Конструктор принимает исходное описание, добавляемые и вычитаемые типы. Значения Null в данных при этом лучше заранее заменить в самом запросе через ЕСТЬNULL().

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

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

  • ⚡ Скопировать() с отбором быстрее, чем цикл с проверкой условия и ЗаполнитьЗначенияСвойств(). Платформа делает отбор внутри себя, без интерпретации встроенного языка на каждой строке.
  • 🗂️ Если отбор повторяется много раз по разным значениям, сначала создайте индекс: ТЗ.Индексы.Добавить("Склад"). Тогда Скопировать(Отбор) и НайтиСтроки() не перебирают всю таблицу.
  • 📦 Для переноса одной колонки целиком используйте пару ВыгрузитьКолонку() / ЗагрузитьКолонку() вместо поэлементного цикла.
  • 🔢 Лишние колонки не копируйте. Укажите во втором параметре только нужные: меньше памяти, быстрее работа.
  • 🧹 Если нужен итог по группам, а не сами строки, ТЗ.Свернуть("Склад", "Количество, Сумма") на копии быстрее ручного суммирования.

Отдельная история — клиент и сервер. Объект ТаблицаЗначений в тонком и веб-клиенте недоступен. Все операции копирования делайте в серверных процедурах (&НаСервере или общий модуль с флагом «Сервер»). Гонять большие таблицы между клиентом и сервером ради копирования — самый дорогой путь.

Таблица на управляемой форме

Реквизит формы типа «Таблица значений» на клиенте — это ДанныеФормыКоллекция, а не таблица значений. Метода СкопироватьКолонки() у неё нет. Порядок такой:

&НаСервере
Процедура ПодготовитьКопиюНаСервере()
    ТЗ = РеквизитФормыВЗначение("Товары");
    ПустаяТЗ = ТЗ.СкопироватьКолонки();
    // работа с ПустаяТЗ
КонецПроцедуры

Колонки реквизита формы задаются в конфигураторе. Программно поменять их состав можно только через ИзменитьРеквизиты(), и это отдельная задача.

Ошибки при копировании структуры и как их избежать

Диагностика: выберите симптом

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

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

Можно ли скопировать структуру дерева значений так же, как таблицы?
У дерева значений нет метода СкопироватьКолонки(). Создайте Новый ДеревоЗначений и перенесите колонки циклом через Колонки.Добавить() с теми же четырьмя параметрами.
Копируются ли заголовки колонок?
Да. И СкопироватьКолонки(), и Скопировать() переносят заголовок и ширину вместе с именем и типом.
Как получить пустую таблицу той же структуры, что у запроса, не выполняя его на всех данных?
Добавьте в текст запроса условие, которое не вернёт строк, например ГДЕ 1 = 0, и выгрузите результат. Колонки и типы будут как у полного запроса.
Чем отличается Скопировать() без параметров от СкопироватьКолонки()?
Скопировать() переносит строки с данными, СкопироватьКолонки() — только колонки. Если нужна пустая таблица, второй метод быстрее и не тратит память на данные.
Можно ли поменять порядок колонок в копии?
Да. Соберите копию циклом в нужном порядке или вставляйте колонки методом Колонки.Вставить() с указанием индекса.