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