Разработка и Конфигурация · 12 мин
Как создать копию таблицы значений в 1С
Способы скопировать таблицу значений в 1С: метод Скопировать(), СкопироватьКолонки(), выгрузка и загрузка. Примеры кода, типичные ошибки и выбор метода.
Таблица значений — один из самых востребованных объектов встроенного языка 1С: в ней накапливают результаты запросов, промежуточные расчёты, данные для печатных форм и обменов. Почти в каждой нетривиальной обработке возникает ситуация, когда нужна вторая таблица с теми же данными или той же структурой — чтобы отсортировать, отфильтровать или изменить копию, не трогая оригинал.
Главный подводный камень здесь — ссылочная семантика. Простое присваивание ТЗ2 = ТЗ1; не создаёт копию: обе переменные начинают указывать на один и тот же объект в памяти, и любое изменение через одну переменную мгновенно видно через другую. Это источник трудноуловимых ошибок, особенно когда таблица передаётся между процедурами.
Ниже разобраны рабочие способы получить настоящую независимую копию: метод Скопировать(), копирование только структуры через СкопироватьКолонки(), перенос через значение в строку и обратно, а также варианты для форм и толстого клиента. Для каждого способа указаны ограничения и типичные ошибки.
Почему простое присваивание не работает
Таблица значений в 1С — объект ссылочного типа. Когда вы пишете НоваяТЗ = СтараяТЗ;, платформа копирует не содержимое, а указатель. В результате обе переменные работают с одной областью памяти: добавили строку через НоваяТЗ — она появилась и в СтараяТЗ.
Особенно неприятно это проявляется при передаче таблицы в процедуру или функцию. Если внутри процедуры таблицу очистили или пересортировали, изменения «протекут» наружу и испортят данные вызывающего кода. Поэтому правило простое: нужна независимость — создавайте копию явным методом, а не присваиванием.
Проверить, что переменные указывают на один объект, можно косвенно: измените таблицу через одну переменную и посмотрите количество строк через другую. Если значения совпали после изменения — это один и тот же объект.
Метод Скопировать() — полная копия с данными
Основной способ получить независимую копию — метод Скопировать(). Он создаёт новый объект таблицы значений, полностью повторяющий структуру колонок и содержимое строк оригинала. Дальнейшие изменения копии никак не затрагивают исходную таблицу.
КопияТЗ = ИсходнаяТЗ.Скопировать();
КопияТЗ.Очистить(); // исходная таблица осталась нетронутой
У метода есть два необязательных параметра, которые делают его гибче. Первый — отбор строк: структура, где ключ — имя колонки, а значение — требуемое значение. Второй — список колонок через запятую. Так можно за один вызов получить не полную копию, а нужный срез данных.
// Копия только по одному контрагенту и только две колонки
Отбор = Новый Структура("Контрагент", ВыбранныйКонтрагент);
КопияТЗ = ИсходнаяТЗ.Скопировать(Отбор, "Номенклатура,Количество");
Учтите ограничение: отбор в параметрах работает только на равенство. Условия вида «больше», «содержит», «в списке» так не построить — для них придётся копировать всё и затем фильтровать в цикле или использовать запрос.
КопияТЗ = ИсходнаяТЗ.Скопировать();
// или с отбором и списком колонок:
Отбор = Новый Структура("Склад", Склад);
КопияТЗ = ИсходнаяТЗ.Скопировать(Отбор, "Номенклатура,Количество");Метод СкопироватьКолонки() — копия только структуры
Часто данные не нужны вовсе — требуется пустая таблица с точно такими же колонками, типами и шириной, чтобы заполнить её по другим правилам. Для этого служит метод СкопироватьКолонки(). Он возвращает новую таблицу с идентичной структурой и нулём строк.
ПустаяКопия = ИсходнаяТЗ.СкопироватьКолонки();
Для Каждого Строка Из ИсходнаяТЗ Цикл
Если Строка.Сумма > 0 Тогда
НоваяСтрока = ПустаяКопия.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
КонецЕсли;
КонецЦикла;
Этот подход удобен, когда фильтрация сложнее, чем отбор на равенство: вы проходите по строкам оригинала, проверяете произвольные условия и переносите в новую таблицу только подходящее. Заодно можно модифицировать значения «на лету» — например, пересчитать суммы по курсу.
Методу можно передать строку со списком колонок — тогда в новой таблице окажутся только указанные колонки, тоже пустые. Это экономит память, если из тридцати колонок оригинала нужны пять.
Копирование через сериализацию и другие альтернативы
Иногда копию нужно не просто создать, а передать между сеансами, сохранить в хранилище значений или отправить через веб-сервис. Здесь помогает сериализация: таблицу преобразуют в строку функцией ЗначениеВСтрокуВнутр(), а восстанавливают через ЗначениеИзСтрокиВнутр(). Полученный объект — полностью независимая копия.
СтрокаХранения = ЗначениеВСтрокуВнутр(ИсходнаяТЗ);
// ... где-то позже или в другом сеансе:
ВосстановленнаяТЗ = ЗначениеИзСтрокиВнутр(СтрокаХранения);
Важное ограничение: эти функции доступны только на толстом клиенте и на сервере, в тонком клиенте их нет. Для тонкого клиента и веб-клиента перенос данных между клиентом и сервером делают через временное хранилище: на сервере таблицу помещают командой ПоместитьВоВременноеХранилище(), на клиенте забирают по адресу.
Ещё один вариант — получить копию через запрос: выгрузить таблицу во временную таблицу запроса и выгрузить результат обратно методом Выгрузить(). Это оправдано, когда попутно нужно сгруппировать, отсортировать или соединить данные, но для простого копирования способ избыточен и медленнее прямого Скопировать().
Сравнение вариантов
Полная независимая копия с данными. Подходит в большинстве случаев; ограничение — отбор только на равенство.
Только структура без строк. Подходит для ручной фильтрации и пересчёта; данные придётся переносить самостоятельно.
Копия через сериализацию для хранения и передачи. Не работает на тонком клиенте, медленнее прямого копирования.
Копия с попутной обработкой — группировкой, соединением. Избыточен, если нужна просто копия.
Копирование таблицы значений на форме
На управляемой форме таблицы значений обычно хранятся как реквизиты формы типа «ТаблицаЗначений» и в коде представлены объектом ДанныеФормыКоллекция. У этого объекта нет метода Скопировать() — попытка вызвать его закончится ошибкой.
Решение — преобразование: метод Выгрузить() превращает реквизит формы в обычную таблицу значений, а Загрузить() — загружает таблицу обратно в реквизит. Последовательность «выгрузить — скопировать — загрузить» даёт полноценную копию:
ТЗ = РеквизитФормыВЗначение("ТаблицаНаФорме");
КопияТЗ = ТЗ.Скопировать();
ЗначениеВРеквизитФормы(КопияТЗ, "КопияТаблицыНаФорме");
Учтите, что РеквизитФормыВЗначение() и ЗначениеВРеквизитФормы() работают только в серверном контексте (директива &НаСервере). На клиенте доступен Выгрузить()/Загрузить() у самой коллекции формы, но обмениваться полными таблицами между клиентом и сервером стоит осознанно — это трафик и время.
Как проверить, что копия получилась корректной
После копирования полезно убедиться в трёх вещах: совпадает ли количество строк, совпадает ли состав колонок и действительно ли объекты независимы. Первые две проверки делаются сравнением Количество() у коллекций строк и колонок.
Независимость проверяется контрольным изменением: добавьте в копию строку или измените значение ячейки, затем сравните количество строк у копии и оригинала. Если у оригинала ничего не изменилось — копия настоящая. Если изменилось — где-то в цепочке использовано присваивание вместо копирования.
Как проверить результат
Значения совпадают сразу после копирования
Копия пустая или строк меньше — вероятно, применён отбор или скопированы только колонки
Изменение копии не меняет оригинал
Изменения видны в обеих переменных — использовано присваивание вместо Скопировать()
Имена и типы колонок совпадают
Часть колонок отсутствует — проверьте параметр со списком колонок
Типичные ошибки при копировании таблиц значений
Самая распространённая ошибка — присваивание вместо копии, о ней сказано выше. Вторая по частоте — попытка вызвать Скопировать() у объекта ДанныеФормыКоллекция на управляемой форме: метод отсутствует, нужна выгрузка в таблицу значений.
Третья группа ошибок связана с отбором в параметрах Скопировать(): разработчик передаёт в структуру отбора условие неравенства или массив и удивляется пустому результату. Отбор работает только на точное совпадение значения. Также колонки в списке указываются строкой через запятую без пробелов в именах — опечатка в имени колонки вызовет исключение.
Наконец, при сериализации через ЗначениеВСтрокуВнутр() забывают, что восстановить значение можно только там, где функция доступна, и что строка внутреннего формата не предназначена для обмена с внешними системами — это закрытый формат платформы.
Как выбрать подходящий способ
Логика выбора сводится к двум вопросам: нужны ли в копии данные и где выполняется код. Нужны данные и структура — берите Скопировать(). Нужна только структура — СкопироватьКолонки(). Работаете с реквизитом формы — сначала выгрузите его в таблицу значений.
Для очень больших таблиц подумайте, можно ли обойтись без копии вообще: индексы, методы НайтиСтроки() и ВыбратьСтроку() позволяют работать с подмножествами данных без дублирования в памяти. Полная копия оправдана, когда оригинал должен остаться гарантированно неизменным.
Как выбрать действие
Что важно запомнить
Присваивание не копирует таблицу значений — для независимой копии используйте Скопировать(), для структуры без данных — СкопироватьКолонки(), для реквизитов формы — выгрузку в таблицу значений. После копирования проверяйте количество строк и независимость объектов контрольным изменением.
Частые вопросы
Почему после «копирования» присваиванием меняются обе таблицы?
Потому что таблица значений — ссылочный объект. Присваивание копирует указатель, а не содержимое: обе переменные работают с одной областью памяти. Используйте метод Скопировать(), чтобы получить действительно отдельный объект.
Можно ли скопировать только часть строк по сложному условию?
Встроенный отбор метода Скопировать() поддерживает только равенство. Для сложных условий создайте пустую таблицу через СкопироватьКолонки() и переносите строки в цикле с проверкой условий, либо используйте запрос с выгрузкой результата.
Как скопировать таблицу значений на управляемой форме?
У реквизита формы нет метода Скопировать(). В серверной процедуре преобразуйте реквизит в таблицу значений через РеквизитФормыВЗначение(), скопируйте её и верните результат через ЗначениеВРеквизитФормы() в другой реквизит.
Сохраняются ли типы колонок при копировании?
Да. Методы Скопировать() и СкопироватьКолонки() полностью сохраняют описание типов, ширину и точность колонок. Если в параметрах указан список колонок, в копию попадут только они — с исходными типами.
Что быстрее: Скопировать() или выгрузка через запрос?
Для простого копирования метод Скопировать() быстрее и потребляет меньше ресурсов — он работает напрямую с объектом в памяти. Запрос оправдан, только когда попутно нужна обработка: группировка, соединение, сортировка на уровне СУБД.
Можно ли передать копию таблицы между клиентом и сервером?
Напрямую таблицу значений между клиентом и сервером не передают. На сервере её помещают во временное хранилище функцией ПоместитьВоВременноеХранилище(), а на клиенте забирают по полученному адресу. Либо работают через реквизиты формы, которые платформа синхронизирует сама.