Разработка и Конфигурация · 11 мин
Хранилище значений в 1С: где хранятся данные и как с ними работать
Где физически хранятся данные типа ХранилищеЗначения в 1С: в таблицах СУБД, в полях реквизитов. Как получить, сохранить и найти значение программно и в конфигураторе.
Тип ХранилищеЗначения в 1С часто вызывает вопросы у начинающих разработчиков: данные вроде бы записаны в реквизит объекта, но при просмотре таблицы базы данных виден лишь непонятный набор байт. Причина проста — хранилище значений не хранит данные «как есть», а сериализует их в специальный бинарный формат и помещает в поле таблицы базы данных.
Физически значение хранится в том же реквизите объекта (справочника, документа, регистра), которому назначен тип ХранилищеЗначения. В клиент-серверном варианте это поле в таблице MS SQL, PostgreSQL или Oracle с типом, предназначенным для двоичных данных (например, varbinary/image в MS SQL). В файловом варианте — внутри файла 1Cv8.1CD. Отдельной «папки» или отдельной таблицы для хранилищ значений платформа не создаёт.
Логически же работа с этим типом идёт только через встроенный язык: конструктор Новый ХранилищеЗначения(Значение) упаковывает данные, а метод Получить() распаковывает их обратно. Ниже разберём, где именно лежат эти данные, как их прочитать, чем хранилище отличается от строки неограниченной длины и какие ошибки встречаются чаще всего.
Что такое ХранилищеЗначения и как оно устроено
ХранилищеЗначения — это объект встроенного языка 1С, который упаковывает произвольное значение (строку, картинку, таблицу значений, двоичные данные, произвольный сериализуемый объект) в единый бинарный контейнер. При записи объекта этот контейнер сохраняется в базу как поток байт вместе со служебным заголовком, описывающим тип содержимого.
При чтении объекта из базы платформа не распаковывает значение автоматически: реквизит содержит объект-обёртку, и чтобы получить исходные данные, нужно вызвать метод Получить(). Это важно для производительности — большие данные не десериализуются, пока они реально не понадобились.
Дополнительно конструктор поддерживает параметр сжатия: Новый ХранилищеЗначения(Данные, Новый СжатиеДанных(9)). Сжатие уменьшает объём занимаемого места, но замедляет запись и чтение. Для небольших значений (например, настроек отчёта) сжатие обычно не требуется.
Где физически находятся данные хранилища
В файловом варианте базы все данные, включая содержимое хранилищ значений, находятся внутри единого файла 1Cv8.1CD в каталоге информационной базы. Прочитать его штатными средствами ОС невозможно — формат закрытый, и доступ к данным выполняется только через платформу.
В клиент-серверном варианте каждый реквизит с типом ХранилищеЗначения отображается на поле таблицы СУБД с двоичным типом. Например, реквизит «ДанныеКартинки» справочника «Товары» будет храниться в поле вида _Fld123 в таблице _Reference45 (реальные имена зависят от структуры конкретной базы). Увидеть соответствие полей можно через функцию ПолучитьСтруктуруХраненияБазыДанных().
Важно понимать: значение хранилища лежит в той же строке таблицы, что и остальные реквизиты объекта (если не используются дополнительные механизмы вроде хранения в регистрах сведений или присоединённых файлов). Поэтому большие хранилища напрямую влияют на размер таблицы и скорость выборок по ней.
Все хранилища значений внутри единого файла базы, доступ только через платформу
Поле типа varbinary/image в таблице объекта, имя поля смотреть через структуру хранения
Данные распаковываются только встроенным языком 1С
Как записать и прочитать значение программно
Запись выполняется в три шага: создать хранилище из значения, присвоить его реквизиту, записать объект. Чтение — получить объект и вызвать Получить() у реквизита. Типичный фрагмент кода:
// Запись картинки в реквизит справочника
Объект = Справочники.Товары.НайтиПоКоду("000001").ПолучитьОбъект();
ДвоичныеДанные = Новый ДвоичныеДанные("C:\Images\photo.jpg");
Объект.ДанныеКартинки = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
Объект.Записать();
// Чтение
СсылкаНаТовар = Справочники.Товары.НайтиПоКоду("000001");
Данные = СсылкаНаТовар.ДанныеКартинки.Получить();
Если Данные <> Неопределено Тогда
Данные.Записать("C:\Temp\photo_out.jpg");
КонецЕсли;
Обратите внимание на проверку результата Получить(): если в реквизит ничего не записывали или записали пустое хранилище, метод вернёт Неопределено. Обращение к методам результата без проверки — частая причина ошибок времени выполнения.
В запросах значение хранилища выбирается как обычное поле, но сравнивать его в условии ГДЕ с конкретным содержимым бессмысленно — сравниваются сериализованные байты, а не исходные значения. Для проверки заполненности используйте конструкцию ГДЕ Реквизит ЕСТЬ NULL или проверку на пустое хранилище.
- 1Убедитесь, что реквизит объекта имеет тип ХранилищеЗначения
- 2Создайте хранилище: Новый ХранилищеЗначения(Данные)
- 3Присвойте хранилище реквизиту и запишите объект
- 4Для чтения получите объект и вызовите Реквизит.Получить()
- 5Проверьте результат на Неопределено перед использованием
Как найти, в какой таблице и поле лежит значение
Если нужно определить физическое расположение данных на уровне СУБД (например, для оценки размера таблиц или миграции), используйте функцию ПолучитьСтруктуруХраненияБазыДанных(). Она возвращает таблицу соответствия объектов метаданных и полей базы данных. Отфильтруйте её по имени метаданных и найдите нужный реквизит — в колонке «ИмяПоляХранения» будет физическое имя поля.
Для разового просмотра удобно вывести структуру хранения в табличный документ и сохранить его. Учтите, что прямое обращение к таблицам СУБД средствами SQL для чтения хранилищ практически бесполезно: формат сериализации закрытый, и распаковать значение вне платформы штатными средствами нельзя.
Изменять содержимое полей напрямую в СУБД категорически не рекомендуется: это нарушает целостность базы, лишает поддержки и может привести к невозможности открыть объект средствами платформы.
ХранилищеЗначения или строка неограниченной длины: что выбрать
Частая альтернатива хранилищу — реквизит типа «Строка» неограниченной длины. Оба варианта позволяют сохранить большой объём текста, но ведут себя по-разному. Строку можно выводить в формы, отчёты и использовать в запросах напрямую; хранилище требует программной распаковки, зато поддерживает любые типы данных и сжатие.
Сравнение вариантов
Подходит для картинок, файлов, настроек отчётов, таблиц значений. Ограничение: данные нельзя вывести в отчёт или отфильтровать в запросе без распаковки
Подходит для длинных текстов, комментариев, XML/JSON. Ограничение: только текст, нет сжатия, при больших объёмах растёт таблица
Подходит для файлов в типовых конфигурациях: файлы хранятся в отдельных таблицах или томах, не раздувая основную таблицу объекта
Если данные представляют собой файлы и конфигурация построена на Библиотеке стандартных подсистем, предпочтительнее механизм присоединённых файлов — он изначально рассчитан на большие объёмы, версии файлов и хранение в томах на диске. ХранилищеЗначения разумно использовать для служебных данных: настроек, сериализованных структур, небольших изображений.
Типичные ошибки и ограничения
Самая распространённая ошибка — попытка вывести реквизит-хранилище в отчёт СКД или динамический список. В списке отобразится строка «ХранилищеЗначения», а не содержимое. Для отображения картинки в форме нужно программно получить данные и поместить их во временное хранилище формы через ПоместитьВоВременноеХранилище().
Вторая группа проблем связана с объёмом. Размещение сотен мегабайт в реквизитах справочника приводит к разрастанию таблицы, замедлению выборок и реиндексации. Признак проблемы — резкий рост размера базы и замедление открытия списков. Решение — вынести большие данные в отдельный регистр сведений или присоединённые файлы.
Если нужно массово проверить, в каких объектах заполнено хранилище, выполните запрос с условием на NULL по полю реквизита и обойдите результат в транзакции. Это быстрее и безопаснее, чем выборка всех объектов с вызовом Получить() у каждого.
Как проверить результат работы
Как проверить результат
После Записать() повторное чтение через Получить() возвращает исходные данные
Получить() возвращает Неопределено — объект не был записан или реквизит не того типа
ТипЗнч(Реквизит) = ХранилищеЗначения до распаковки
Реквизит содержит строку или Неопределено — тип реквизита в метаданных не ХранилищеЗначения
Рост размера таблицы соответствует объёму записываемых данных
Таблица растёт аномально быстро — вероятно, дублируется запись больших файлов
Итоговая проверка проста: после записи откройте объект заново (либо в новом сеансе), вызовите Получить() и сравните результат с исходным значением. Для файлов достаточно сохранить их на диск и открыть штатной программой. Если данные читаются корректно — хранилище работает как задумано.
Что важно запомнить
Данные ХранилищеЗначения лежат в бинарном виде прямо в поле реквизита таблицы базы: внутри 1Cv8.1CD в файловом режиме или в поле СУБД в клиент-серверном. Читаются они только методом Получить(), напрямую из СУБД не распаковываются. Для больших файлов предпочтительнее присоединённые файлы или отдельный регистр, а любые массовые операции выполняйте после резервного копирования.
Частые вопросы
Можно ли увидеть содержимое хранилища значений в конфигураторе?
Нет, штатного просмотра сериализованного содержимого в конфигураторе нет. Нужно написать небольшую обработку или выполнить код через консоль кода: получить объект и вызвать у реквизита метод Получить(), затем вывести или сохранить результат.
Занимает ли пустое хранилище значений место в базе?
Пустое или незаполненное хранилище занимает минимальный объём — фактически служебный заголовок или NULL. Заметный рост размера базы дают только реально записанные данные, особенно несжатые файлы и изображения.
Можно ли хранить в ХранилищеЗначения таблицу значений или структуру?
Таблицу значений — да, она сериализуется штатно и восстанавливается методом Получить(). Структуру напрямую сериализовать нельзя: её предварительно преобразуют, например, в текст JSON через ЗаписьJSON, и сохраняют уже строку.
Почему в запросе поле хранилища возвращает «ХранилищеЗначения» вместо данных?
Запрос возвращает объект-обёртку, а не распакованное содержимое. Обойдите выборку и для каждой строки вызовите Получить(). Учитывайте, что массовая распаковка больших значений в цикле может быть ресурсоёмкой.
Как перенести данные из хранилища значений в другую базу?
Через обмен данными, конвертацию данных или собственную обработку: прочитайте значение методом Получить() в базе-источнике, передайте его (для файлов — как двоичные данные) и в базе-приёмнике создайте новое хранилище через Новый ХранилищеЗначения(). Прямое копирование бинарных полей между СУБД не сработает.