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