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

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

Хранилище значений в 1С: где хранятся данные и как с ними работать

Где физически хранятся данные типа ХранилищеЗначения в 1С: в таблицах СУБД, в полях реквизитов. Как получить, сохранить и найти значение программно и в конфигураторе.

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

Физически значение хранится в том же реквизите объекта (справочника, документа, регистра), которому назначен тип ХранилищеЗначения. В клиент-серверном варианте это поле в таблице MS SQL, PostgreSQL или Oracle с типом, предназначенным для двоичных данных (например, varbinary/image в MS SQL). В файловом варианте — внутри файла 1Cv8.1CD. Отдельной «папки» или отдельной таблицы для хранилищ значений платформа не создаёт.

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

Что такое ХранилищеЗначения и как оно устроено

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

При чтении объекта из базы платформа не распаковывает значение автоматически: реквизит содержит объект-обёртку, и чтобы получить исходные данные, нужно вызвать метод Получить(). Это важно для производительности — большие данные не десериализуются, пока они реально не понадобились.

Дополнительно конструктор поддерживает параметр сжатия: Новый ХранилищеЗначения(Данные, Новый СжатиеДанных(9)). Сжатие уменьшает объём занимаемого места, но замедляет запись и чтение. Для небольших значений (например, настроек отчёта) сжатие обычно не требуется.

Где физически находятся данные хранилища

В файловом варианте базы все данные, включая содержимое хранилищ значений, находятся внутри единого файла 1Cv8.1CD в каталоге информационной базы. Прочитать его штатными средствами ОС невозможно — формат закрытый, и доступ к данным выполняется только через платформу.

В клиент-серверном варианте каждый реквизит с типом ХранилищеЗначения отображается на поле таблицы СУБД с двоичным типом. Например, реквизит «ДанныеКартинки» справочника «Товары» будет храниться в поле вида _Fld123 в таблице _Reference45 (реальные имена зависят от структуры конкретной базы). Увидеть соответствие полей можно через функцию ПолучитьСтруктуруХраненияБазыДанных().

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

Файловая базаФайл 1Cv8.1CD

Все хранилища значений внутри единого файла базы, доступ только через платформу

Клиент-серверБинарное поле таблицы СУБД

Поле типа varbinary/image в таблице объекта, имя поля смотреть через структуру хранения

Логический доступМетод Получить()

Данные распаковываются только встроенным языком 1С

Как записать и прочитать значение программно

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

// Запись картинки в реквизит справочника

Объект = Справочники.Товары.НайтиПоКоду("000001").ПолучитьОбъект();

ДвоичныеДанные = Новый ДвоичныеДанные("C:\Images\photo.jpg");

Объект.ДанныеКартинки = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));

Объект.Записать();

// Чтение

СсылкаНаТовар = Справочники.Товары.НайтиПоКоду("000001");

Данные = СсылкаНаТовар.ДанныеКартинки.Получить();

Если Данные <> Неопределено Тогда

Данные.Записать("C:\Temp\photo_out.jpg");

КонецЕсли;

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

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

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

Как найти, в какой таблице и поле лежит значение

Если нужно определить физическое расположение данных на уровне СУБД (например, для оценки размера таблиц или миграции), используйте функцию ПолучитьСтруктуруХраненияБазыДанных(). Она возвращает таблицу соответствия объектов метаданных и полей базы данных. Отфильтруйте её по имени метаданных и найдите нужный реквизит — в колонке «ИмяПоляХранения» будет физическое имя поля.

Для разового просмотра удобно вывести структуру хранения в табличный документ и сохранить его. Учтите, что прямое обращение к таблицам СУБД средствами SQL для чтения хранилищ практически бесполезно: формат сериализации закрытый, и распаковать значение вне платформы штатными средствами нельзя.

Изменять содержимое полей напрямую в СУБД категорически не рекомендуется: это нарушает целостность базы, лишает поддержки и может привести к невозможности открыть объект средствами платформы.

ХранилищеЗначения или строка неограниченной длины: что выбрать

Частая альтернатива хранилищу — реквизит типа «Строка» неограниченной длины. Оба варианта позволяют сохранить большой объём текста, но ведут себя по-разному. Строку можно выводить в формы, отчёты и использовать в запросах напрямую; хранилище требует программной распаковки, зато поддерживает любые типы данных и сжатие.

Сравнение вариантов

ХранилищеЗначения

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

Строка неограниченной длины

Подходит для длинных текстов, комментариев, XML/JSON. Ограничение: только текст, нет сжатия, при больших объёмах растёт таблица

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

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

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

Типичные ошибки и ограничения

Самая распространённая ошибка — попытка вывести реквизит-хранилище в отчёт СКД или динамический список. В списке отобразится строка «ХранилищеЗначения», а не содержимое. Для отображения картинки в форме нужно программно получить данные и поместить их во временное хранилище формы через ПоместитьВоВременноеХранилище().

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

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

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

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

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

Запись значения
Норма

После Записать() повторное чтение через Получить() возвращает исходные данные

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

Получить() возвращает Неопределено — объект не был записан или реквизит не того типа

Корректность типа
Норма

ТипЗнч(Реквизит) = ХранилищеЗначения до распаковки

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

Реквизит содержит строку или Неопределено — тип реквизита в метаданных не ХранилищеЗначения

Размер базы
Норма

Рост размера таблицы соответствует объёму записываемых данных

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

Таблица растёт аномально быстро — вероятно, дублируется запись больших файлов

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

Итог

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

Данные ХранилищеЗначения лежат в бинарном виде прямо в поле реквизита таблицы базы: внутри 1Cv8.1CD в файловом режиме или в поле СУБД в клиент-серверном. Читаются они только методом Получить(), напрямую из СУБД не распаковываются. Для больших файлов предпочтительнее присоединённые файлы или отдельный регистр, а любые массовые операции выполняйте после резервного копирования.

Частые вопросы

Можно ли увидеть содержимое хранилища значений в конфигураторе?

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

Занимает ли пустое хранилище значений место в базе?

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

Можно ли хранить в ХранилищеЗначения таблицу значений или структуру?

Таблицу значений — да, она сериализуется штатно и восстанавливается методом Получить(). Структуру напрямую сериализовать нельзя: её предварительно преобразуют, например, в текст JSON через ЗаписьJSON, и сохраняют уже строку.

Почему в запросе поле хранилища возвращает «ХранилищеЗначения» вместо данных?

Запрос возвращает объект-обёртку, а не распакованное содержимое. Обойдите выборку и для каждой строки вызовите Получить(). Учитывайте, что массовая распаковка больших значений в цикле может быть ресурсоёмкой.

Как перенести данные из хранилища значений в другую базу?

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