К статье
Практический материал

Разработка и Конфигурация · 11 мин

Хранилище значений 1С: назначение, применение и примеры

Хранилище значений 1С: что это за тип данных, как создать, записать и извлечь значение, примеры кода, типичные ошибки и рекомендации по использованию.

Хранилище значений в 1С — это специальный тип данных, предназначенный для упаковки произвольных значений в единый контейнер с возможностью сериализации, то есть преобразования в поток байтов. С его помощью в поле объекта конфигурации можно сохранить практически любые данные: картинки, файлы, таблицы значений, списки настроек, результаты запросов и даже сложные структуры.

Главная особенность типа «ХранилищеЗначения» в том, что содержимое извлекается из него только целиком. Нельзя обратиться к отдельному элементу массива или строке таблицы, пока значение не извлечено методом Получить(). Это одновременно и ограничение, и преимущество: платформа не тратит ресурсы на разбор содержимого, пока оно не понадобилось.

Тип широко используется в типовых конфигурациях: фотографии товаров в справочнике «Номенклатура», присоединённые файлы, сохранённые настройки отчётов, кэшированные данные обменов — всё это хранится именно в реквизитах с типом «ХранилищеЗначения». Понимание механизма необходимо разработчику при доработке конфигураций и администратору при оценке размера базы.

Как устроен тип «ХранилищеЗначения»

Технически хранилище значений — это обёртка, которая при записи в базу данных преобразует содержимое в двоичный формат. В таблицах СУБД такое поле обычно имеет тип BLOB (binary large object) или его аналог. Платформа 1С сама выполняет сериализацию при помещении значения в хранилище и десериализацию при вызове метода Получить().

Конструктор типа принимает два параметра: само значение и необязательный параметр сжатия — значение системного перечисления СжатиеДанных. По умолчанию применяется сжатие СжатиеДанных.Быстродействие (в ряде версий платформы — без сжатия), поэтому для больших данных имеет смысл явно указывать СжатиеДанных.Сжатие, чтобы уменьшить объём в базе.

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

Какие данные можно поместить в хранилище

Платформа позволяет сериализовать большинство встроенных типов: примитивы (строки, числа, даты, булево), массивы, структуры, соответствия, таблицы и деревья значений, списки значений, картинки, двоичные данные, табличные документы, результаты запросов, объекты типа «СхемаКомпоновкиДанных» и многие другие.

Есть и ограничения. Нельзя поместить в хранилище ссылки на объекты базы в переносимом виде так, чтобы при обмене они корректно восстановились в другой базе: сериализованная ссылка содержит внутренний идентификатор, который в другой информационной базе укажет на другой объект или ни на что. Также не сериализуются объекты, привязанные к контексту сеанса, — например, элементы форм и некоторые COM-объекты.

Типичное практическое применение:

  • изображения и файлы, прикреплённые к элементам справочников;
  • настройки пользователей и варианты отчётов;
  • кэш результатов тяжёлых вычислений;
  • пакеты данных для фоновой обработки и обменов;
  • снимки состояния документов для аудита изменений.

Как создать, записать и прочитать хранилище значений

Работа с типом сводится к трём операциям: создание через конструктор, присвоение реквизиту объекта и извлечение методом Получить(). Ниже — типовой пример сохранения картинки в реквизит справочника.

bsl
// Запись
Картинка = Новый Картинка("C:\Images\photo.jpg");
ОбъектТовара = ТоварСсылка.ПолучитьОбъект();
ОбъектТовара.Фото = Новый ХранилищеЗначения(Картинка, Новый СжатиеДанных(9));
ОбъектТовара.Записать();

// Чтение
Запрос = Новый Запрос("ВЫБРАТЬ Фото ИЗ Справочник.Номенклатура ГДЕ Ссылка = &Ссылка");
Запрос.УстановитьПараметр("Ссылка", ТоварСсылка);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
    КартинкаИзБазы = Выборка.Фото.Получить();
КонецЕсли;

Обратите внимание на чтение: если реквизит не заполнен, метод Получить() вернёт Неопределено. Перед использованием результата всегда проверяйте его на Неопределено, иначе обращение к методам или свойствам полученного значения вызовет ошибку выполнения.

В запросах хранилище значений можно выбирать как обычное поле, но нельзя сравнивать содержимое, искать по нему или использовать в условиях по смыслу — доступна только проверка на заполненность через конструкции вида НЕ Хранилище ЕСТЬ NULL в сочетании с соединениями, либо проверка после выборки.

Порядок действий при сохранении файла в хранилище
  1. 1
    Прочитайте файл в ДвоичныеДанные или Картинку
  2. 2
    Создайте хранилище: Новый ХранилищеЗначения(Данные, Сжатие)
  3. 3
    Присвойте хранилище реквизиту объекта и вызовите Записать()
  4. 4
    Для проверки прочитайте реквизит и вызовите Получить()
  5. 5
    Сравните размер или контрольную сумму результата с исходным файлом

Где применяется в конфигурации и как добавить реквизит

Чтобы использовать хранилище в собственной разработке, добавьте объекту метаданных (справочнику, документу, регистру сведений) реквизит с типом «ХранилищеЗначения». В дереве конфигурации это делается через палитру свойств реквизита: выберите тип, снимите лишние флажки составного типа, оставив только «ХранилищеЗначения».

Для регистров сведений хранилище часто используют как ресурс: измерение — ссылка на объект, ресурс — хранилище с данными. Такая схема применяется, например, для хранения нескольких изображений одного товара или версий присоединённых файлов, когда одного реквизита справочника недостаточно.

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

Хранилище значений или отдельные реквизиты

Хранилище значений

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

Примитивные реквизиты

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

Присоединённые файлы (БСП)

Подходят для файлов в типовых конфигурациях: готовые механизмы хранения, версий и подписей. Ограничение: требуют подсистемы БСП.

Производительность, размер базы и типичные риски

Главная цена использования хранилища — рост объёма базы и нагрузка при чтении. Каждое обращение к реквизиту с последующим Получить() означает чтение BLOB-поля и десериализацию всего содержимого. Если в запросе выбрать хранилище для тысячи строк, платформа вычитает тысячу двоичных объектов — это заметно медленнее, чем выборка примитивных полей.

Практические рекомендации по снижению нагрузки:

  • выбирайте поле с хранилищем в запросе только тогда, когда оно действительно нужно;
  • явно указывайте сжатие для больших данных — это уменьшает объём на диске;
  • не храните в хранилище то, что можно вычислить заново за приемлемое время;
  • для больших файлов рассмотрите тома хранения файлов (в типовых) вместо размещения в базе.

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

Как проверить содержимое и отладить проблемы

Отладка хранилища неудобна тем, что в табло и в форме содержимое не отображается — видна только надпись «Хранилище значений». Чтобы увидеть, что внутри, извлеките значение методом Получить() и выведите его тип через ТипЗнч(), а затем исследуйте само значение.

Признаки типичных проблем: Получить() возвращает Неопределено (реквизит пуст или значение не записано), ошибка десериализации при чтении (данные повреждены или записаны внешним инструментом в обход платформы), неожиданный тип результата (в реквизит писали разные типы в разных обработчиках).

Контрольные точки

Как проверить результат

Результат Получить()
Норма

Возвращается значение ожидаемого типа

Тревожный признак

Неопределено или ошибка десериализации

Тип через ТипЗнч()
Норма

Совпадает с типом, который помещали при записи

Тревожный признак

Другой тип — значит, реквизит перезаписывали из другого места

Размер базы после загрузки
Норма

Рост соответствует объёму загруженных данных с учётом сжатия

Тревожный признак

Рост в разы больше объёма файлов — сжатие не применяется

Типичные ошибки при работе с хранилищем

Большинство проблем с хранилищем значений связано не с самим типом, а с неверными ожиданиями от его поведения. Разбор частых ошибок помогает избежать потери данных и лишней нагрузки.

Типичные ошибки
01
Отсутствие проверки на Неопределено после Получить() — падение кода на пустых реквизитах
02
Выборка поля с хранилищем в запросах по спискам — чтение тысяч BLOB и просадка производительности
03
Хранение ссылок в сериализованных структурах с последующим переносом в другую базу — битые ссылки
04
Запись без сжатия больших файлов — неконтролируемый рост размера базы
05
Ожидание, что изменение исходной таблицы обновит хранилище — хранилище держит копию на момент создания

Отдельно стоит предупредить о попытках редактировать содержимое BLOB-полей напрямую средствами СУБД. Платформа хранит данные в собственном формате сериализации, и любое внешнее вмешательство почти гарантированно приведёт к ошибкам десериализации или повреждению данных.

Частые вопросы о хранилище значений 1С

Чем хранилище значений отличается от реквизита типа «Строка неограниченной длины»?

Неограниченная строка хранит только текст и позволяет выполнять поиск по подстроке. Хранилище значений принимает любые сериализуемые типы — картинки, таблицы, файлы, — но не поддерживает поиск по содержимому и извлекается только целиком.

Можно ли найти записи по содержимому хранилища значений?

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

Что возвращает метод Получить(), если реквизит не заполнен?

Значение Неопределено. Поэтому перед обращением к свойствам и методам результата всегда выполняйте проверку, иначе получите ошибку выполнения в момент обращения.

Нужно ли указывать сжатие при создании хранилища?

Для небольших структур сжатие почти не влияет на результат. Для файлов и больших таблиц явное указание сжатия заметно уменьшает объём в базе за счёт небольшого роста времени записи и чтения.

Можно ли хранить в хранилище ссылки на объекты базы?

Технически ссылки сериализуются, но переносить такое хранилище между разными базами нельзя: внутренние идентификаторы не совпадут, и после десериализации ссылки станут недействительными. Внутри одной базы это допустимо.

Как узнать, что именно лежит в хранилище конкретной записи?

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

Итог

Что важно запомнить

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