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