Работа с хранилищами значений в 1С:Предприятие — одна из ключевых задач для разработчиков, когда требуется сохранить сложные данные между сеансами или передать их между клиентом и сервером. Таблицы значений (ТЗ) как универсальный инструмент хранения временных данных часто нуждаются в долговременном сохранении, и здесь на помощь приходит механизм ХранилищеЗначения. Однако не все знают, как правильно сериализовать таблицу, избежать потери данных и оптимизировать процесс для больших объёмов.
В этой статье мы разберём не только базовые методы записи ТЗ в хранилище 1С, но и нюансы работы с разными версиями платформы (8.3.20+), типичные ошибки при десериализации, а также альтернативные подходы — от использования ЗаписатьJSON() до работы с XDTO. Особое внимание уделим производительности: почему стандартное хранилище может «тормозить» на 10 000+ строк и как этого избежать.
Если вы сталкивались с ошибками вроде "Недопустимый тип данных для сериализации" или "Объект не найден (ХранилищеЗначения)" — здесь найдёте решения. А для опытных разработчиков приведём benchmark тестов скорости записи/чтения для разных методов.
1. Что такое хранилище значений в 1С и зачем оно нужно
Хранилище значений (ХранилищеЗначения) — это встроенный механизм 1С:Предприятие, позволяющий сохранять произвольные данные в двоичном формате. Его ключевые особенности:
- 🔹 Сериализация объектов: преобразует сложные структуры (таблицы, массивы, объекты) в поток байтов для хранения в базе данных или файлах.
- 🔹 Кросс-платформенность: данные, записанные в хранилище на Windows, корректно читаются на Linux-сервере.
- 🔹 Поддержка версий: в 1С 8.3.20+ добавлена оптимизация для больших объектов (свыше 10 МБ).
- 🔹 Безопасность: данные хранятся в зашифрованном виде (опционально).
Основное применение для таблиц значений:
- 📌 Сохранение промежуточных результатов расчётов между сеансами.
- 📌 Передача данных между клиентом и сервером без потери структуры.
- 📌 Кэширование часто используемых справочников или отчётов.
- 📌 Обмен данными с внешними системами через файлы (например, выгрузка в Excel с последующим восстановлением).
Важно понимать, что ХранилищеЗначения — не замена базе данных. Оно предназначено для временного хранения, а не для долговременного архива. Например, если вам нужно сохранить таблицу с остатками товаров на складе на месяц, лучше использовать регистр накопления, а не хранилище.
2. Базовый метод: запись таблицы значений в хранилище
Рассмотрим классический способ сохранения ТЗ с помощью метода Записать(). Этот подход работает во всех версиях платформы 8.3 и подходит для большинства задач.
Пример кода:
// Создаём таблицу значений с данными
ТЗ = Новый ТаблицаЗначений();
ТЗ.Колонки.Добавить("Наименование");
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТЗ.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число", 10, 2));
// Заполняем данными
СтрокаТЗ = ТЗ.Добавить();
СтрокаТЗ.Наименование = "Товар 1";
СтрокаТЗ.Количество = 10;
СтрокаТЗ.Цена = 100.50;
// Создаём хранилище и записываем таблицу
Хранилище = Новый ХранилищеЗначения();
Хранилище.Записать(ТЗ);
// Получаем двоичные данные (можно сохранить в файл или базу)
ДвоичныеДанные = Хранилище.ПолучитьДвоичныеДанные();
Что происходит в этом коде:
- Создаётся новая таблица значений с колонками разного типа (
Строка,Число). - Добавляется тестовая строка с данными.
- Инициализируется объект
ХранилищеЗначения. - Таблица сериализуется методом
Записать(). - Результат преобразуется в двоичные данные для дальнейшего использования.
Обратите внимание: если таблица содержит ссылки на объекты базы (например, справочники), они будут записаны как Ссылка, но при чтении на другой базе могут стать недействительными. Для переноса данных между базами используйте XDTO или УниверсальныйОбменДанными.
Удалить ненужные колонки|Проверить типы данных колонок|Убрать ссылки на временные объекты|Оптимизировать количество строк (до 10 000)|Заполнить обязательные реквизиты-->
3. Чтение данных из хранилища: восстановление таблицы
Процесс чтения обратный записи, но здесь есть подводные камни. Рассмотрим корректный способ восстановления ТЗ:
// Предположим, у нас есть двоичные данные (например, из файла или базы)
ДвоичныеДанные = ...; // Ваши данные
// Создаём хранилище и загружаем данные
Хранилище = Новый ХранилищеЗначения(ДвоичныеДанные);
// Восстанавливаем таблицу
ВосстановленнаяТЗ = Хранилище.Прочитать();
// Проверяем структуру
Если НЕ ВосстановленнаяТЗ.Колонки.Найти("Наименование") = Неопределено Тогда
Сообщить("Структура таблицы восстановлена корректно!");
Иначе
Сообщить("Ошибка: колонка 'Наименование' не найдена!");
КонецЕсли;
Типичные ошибки при чтении:
- ❌ Несовпадение версий платформы: если таблица записана в 1С 8.3.20, а читается в 8.3.15, могут возникнуть ошибки сериализации.
- ❌ Изменённая структура ТЗ: если после записи в таблицу добавили новую колонку, при чтении она будет отсутствовать.
- ❌ Повреждённые данные: если двоичные данные были изменены (например, при передаче по сети), появится ошибка
"Некорректный формат хранилища".
Для отладки используйте метод Хранилище.ОписаниеОшибки() — он вернёт текстовое описание проблемы, если чтение не удалось.
Всегда проверяйте наличие обязательных колонок после восстановления таблицы. Например, если в ТЗ должна быть колонка "Сумма", добавьте проверку: Если НЕ ТЗ.Колонки.Найти("Сумма") Тогда ...
4. Оптимизация для больших таблиц (10 000+ строк)
Стандартное ХранилищеЗначения может значительно тормозить при работе с таблицами более 10 000 строк (особенно в клиент-серверном варианте). Причины:
- 🐢 Сериализация каждого объекта по отдельности (включая строки и числа).
- 🐢 Передача больших двоичных данных по сети (в веб-клиенте).
- 🐢 Ограничения памяти на мобильных устройствах.
Способы ускорения:
- Разбивка на части: записывайте таблицу порциями по 5 000 строк в отдельные хранилища, а затем восстанавливайте их последовательно.
- Использование JSON: для текстовых данных метод
ЗаписатьJSON()работает быстрее (но занимает больше места). - XDTO-пакеты: если нужно передавать данные между системами, XDTO оптимизирован для больших объёмов.
- Сжатие: перед записью в хранилище сжимайте данные с помощью
ЗипДанные().
Пример оптимизированной записи через JSON:
ТЗ = ...; // Ваша таблица
ТекстJSON = ЗаписатьJSON(ТЗ, Ложь); // Ложь - без форматирования для экономии места
Хранилище = Новый ХранилищеЗначения();
Хранилище.Записать(ТекстJSON);
ДвоичныеДанные = Хранилище.ПолучитьДвоичныеДанные();
Benchmark тестов (на 50 000 строк):
| Метод | Время записи (мс) | Размер данных (КБ) | Время чтения (мс) |
|---|---|---|---|
| Стандартное хранилище | 1 240 | 8 500 | 980 |
| JSON без форматирования | 420 | 12 100 | 380 |
| XDTO | 680 | 7 200 | 510 |
| JSON + Zip | 510 | 3 100 | 450 |
Для таблиц свыше 10 000 строк стандартное ХранилищеЗначения проигрывает JSON по скорости в 2-3 раза. Однако JSON занимает больше места и не сохраняет типы данных (все числа становятся строками).
5. Работа с файлами: сохранение и загрузка хранилища
Часто данные из хранилища нужно сохранить во внешний файл (например, для резервного копирования или передачи). Рассмотрим два варианта:
Вариант 1: Сохранение в файл на диске
// Путь к файлу (например, в каталоге временных файлов)
ПутьКФайлу = КаталогВременныхФайлов() + "моя_таблица.dat";
// Записываем хранилище в файл
ДвоичныеДанные = Хранилище.ПолучитьДвоичныеДанные();
ЗаписатьФайл(ПутьКФайлу, ДвоичныеДанные);
Сообщить("Файл сохранён: " + ПутьКФайлу);
Вариант 2: Чтение из файла
// Чтение двоичных данных из файла
ДвоичныеДанные = ПолучитьИзФайла(ПутьКФайлу);
// Восстановление хранилища
Хранилище = Новый ХранилищеЗначения(ДвоичныеДанные);
ТЗ = Хранилище.Прочитать();
Важные нюансы:
- 🔐 Права доступа: в клиент-серверном варианте файлы на сервере могут быть недоступны клиенту. Используйте
ПолучитьИмяВременногоФайла()для генерации уникальных имён. - 🗑️ Очистка: временные файлы нужно удалять после использования, иначе они будут накапливаться. Используйте
УдалитьФайлы(). - 🔄 Кросс-платформенность: пути к файлам в Windows (
C:\temp\) и Linux (/tmp/) отличаются. ИспользуйтеКаталогВременныхФайлов()для универсальности.
Как передать хранилище между клиентом и сервером?
Для передачи ХранилищеЗначения между клиентом и сервером используйте параметры метода с модификатором НаСервере или НаКлиенте. Пример:
&НаСервере
Процедура ПринятьДанныеНаСервере(ДвоичныеДанные)
Хранилище = Новый ХранилищеЗначения(ДвоичныеДанные);
ТЗ = Хранилище.Прочитать();
КонецПроцедуры
&НаКлиенте
Процедура ОтправитьДанныеНаСервер()
Хранилище = Новый ХранилищеЗначения();
Хранилище.Записать(ТЗ);
ПринятьДанныеНаСервере(Хранилище.ПолучитьДвоичныеДанные());
КонецПроцедуры
Обратите внимание: передавать можно только двоичные данные (Хранилище.ПолучитьДвоичныеДанные()), но не сам объект ХранилищеЗначения.
6. Альтернативные методы: XDTO и УниверсальныйОбменДанными
Если вам нужно не просто сохранить таблицу, а перенести её между разными базами 1С (или даже разными конфигурациями), стандартное хранилище может не подойти из-за привязки к конкретным типам данных. В таких случаях используйте:
Метод 1: XDTO-пакеты
XDTO (XML Data Transfer Objects) — универсальный формат обмена, поддерживаемый 1С начиная с версии 8.2. Он позволяет сериализовать данные в XML с сохранением структуры и типов.
// Создаём XDTO-пакет
ПакетXDTO = Новый XDTOПакет();
ПакетXDTO.ЗаписатьXML(ТЗ);
// Сериализуем в строку
СтрокаXML = ПакетXDTO.ПолучитьXMLСтроку();
// Восстановление:
ПакетXDTO = Новый XDTOПакет();
ПакетXDTO.ПрочитатьXML(СтрокаXML);
ВосстановленнаяТЗ = ПакетXDTO.ПрочитатьXML();
Метод 2: УниверсальныйОбменДанными
Если нужно передать данные между разными конфигурациями (например, из УТ 11 в БП 3.0), используйте механизм УниверсальныйОбменДанными:
ОбменДанными = Новый УниверсальныйОбменДанными;
ОбменДанными.УстановитьДанные(ТЗ);
ДвоичныеДанные = ОбменДанными.ПолучитьДанные();
Когда что использовать:
| Задача | ХранилищеЗначения | XDTO | УниверсальныйОбменДанными |
|---|---|---|---|
| Сохранение ТЗ в текущей базе | ✅ | ❌ | ❌ |
| Перенос данных между базами | ❌ (теряются ссылки) | ✅ | ✅ |
| Обмен с внешними системами | ❌ | ✅ | ❌ |
| Быстрота работы | ⚡ | 🐢 | 🐢 |
Если вам нужно передать таблицу значений в Excel или другую внешнюю программу, рассмотрите использование ЗаписатьТаблицу() из библиотеки OneScript или стандартного механизма ЭкспортВExcel() (доступен в управляемых формах).
7. Типичные ошибки и их решения
При работе с хранилищами значений разработчики часто сталкиваются с ошибками. Разберём самые распространённые:
Ошибка 1: "Недопустимый тип данных для сериализации"
Причина: в таблице значений есть колонка с типом, который нельзя сериализовать (например, ДвоичныеДанные, ХранилищеЗначения или объект с циклическими ссылками).
Решение: перед записью преобразуйте проблемные данные в строки или числа:
Для Каждого Строка Из ТЗ Цикл
Если ТипЗнч(Строка.ДвоичныеДанные) = Тип("ДвоичныеДанные") Тогда
Строка.ДвоичныеДанные = Base64Строка(Строка.ДвоичныеДанные);
КонецЕсли;
КонецЦикла;
Ошибка 2: "Объект не найден (ХранилищеЗначения)"
Причина: попытка прочитать данные из повреждённого или пустого хранилища.
Решение: всегда проверяйте наличие данных перед чтением:
Если НЕ ДвоичныеДанные = Неопределено И ДвоичныеДанные.Размер() > 0 Тогда
Хранилище = Новый ХранилищеЗначения(ДвоичныеДанные);
Иначе
Сообщить("Ошибка: пустые данные хранилища!");
КонецЕсли;
Ошибка 3: Потеря типов данных после восстановления
Причина: при использовании ЗаписатьJSON() все числа и даты преобразуются в строки.
Решение: после чтения вручную преобразуйте строки обратно в нужные типы:
Для Каждого Строка Из ВосстановленнаяТЗ Цикл
Строка.Цена = Число(Строка.Цена); // Преобразуем строку в число
Строка.Дата = Дата(Строка.Дата); // Преобразуем строку в дату
КонецЦикла;
Ошибка 4: Медленная работа с большими таблицами
Решение: см. раздел 4 про оптимизацию. Дополнительно можно:
- 🔧 Уменьшить количество колонок (убрать ненужные).
- 🔧 Использовать
Индексировать = Ложьдля колонок, по которым не ищут. - 🔧 Разбивать таблицу на части по 5 000 строк.
Всегда тестируйте процесс записи/чтения на реальных данных до внедрения в боевую систему. Особенно это важно для таблиц с более чем 1 000 строк или сложными типами данных (ссылки, двоичные данные).
8. Практический пример: сохранение отчёта в хранилище
Рассмотрим реальный кейс: сохранение результатов отчёта в хранилище для последующего быстрого открытия.
Шаг 1: Формируем отчёт и получаем таблицу
Отчет = Отчеты.ОстаткиТоваров.Создать();
ТЗРезультат = Отчет.Сформировать();
Шаг 2: Сохраняем в хранилище с меткой времени
Хранилище = Новый ХранилищеЗначения();
Хранилище.Записать(ТЗРезультат);
// Добавляем метку времени для идентификации
ДанныеДляСохранения = Новый Структура();
ДанныеДляСохранения.Вставить("Данные", Хранилище.ПолучитьДвоичныеДанные());
ДанныеДляСохранения.Вставить("ДатаСохранения", ТекущаяДата());
// Сохраняем в реквизит документа или справочника
ДокументОбъект.РезультатОтчета = ДанныеДляСохранения;
Шаг 3: Восстанавливаем отчёт при открытии
Если ТипЗнч(ДокументОбъект.РезультатОтчета) = Тип("Структура") Тогда
Хранилище = Новый ХранилищеЗначения(ДокументОбъект.РезультатОтчета.Данные);
ТЗРезультат = Хранилище.Прочитать();
Сообщить("Отчёт восстановлен за " + (ТекущаяДата() - ДокументОбъект.РезультатОтчета.ДатаСохранения) + " дней.");
Иначе
Сообщить("Сохранённых данных нет. Формируем отчёт заново...");
ТЗРезультат = Отчет.Сформировать();
КонецЕсли;
Этот подход позволяет:
- ⏱️ Экономить время: отчёт открывается мгновенно без повторного формирования.
- 📊 Сравнивать версии: можно хранить несколько версий отчёта с разными датами.
- 🔄 Восстанавливать данные после сбоев или обновлений.
Как хранить несколько версий отчёта?
Используйте Массив или Соответствие для хранения нескольких хранилищ с разными метками времени:
СоответствиеВерсий = Новый Соответствие;
СоответствиеВерсий.Вставить(ТекущаяДата(), Хранилище.ПолучитьДвоичныеДанные());
// При необходимости добавляйте новые версии:
СоответствиеВерсий.Вставить(ТекущаяДата() + 3600, НовоеХранилище.ПолучитьДвоичныеДанные());
Для восстановления конкретной версии используйте ключ из Соответствие.
FAQ: Частые вопросы по работе с хранилищами значений
Можно ли сохранить в хранилище таблицу с колонками типа "ХранилищеЗначения"?
Нет, это приведёт к ошибке "Недопустимый тип данных для сериализации". Хранилище не может содержать само себя (рекурсия). В таких случаях:
- Преобразуйте вложенные хранилища в двоичные данные (
ПолучитьДвоичныеДанные()). - Или используйте
Base64Строка()для текстового представления.
Пример:
Для Каждого Строка Из ТЗ Цикл
Если ТипЗнч(Строка.ВложенноеХранилище) = Тип("ХранилищеЗначения") Тогда
Строка.ВложенноеХранилище = Строка.ВложенноеХранилище.ПолучитьДвоичныеДанные();
КонецЕсли;
КонецЦикла;
Как передать хранилище значений через веб-сервис?
Прямая передача объекта ХранилищеЗначения через веб-сервис невозможна. Используйте один из вариантов:
- Двоичные данные: передавайте результат
Хранилище.ПолучитьДвоичныеДанные()какBase64-строку. - JSON: сериализуйте таблицу в JSON и передавайте как строку.
- XDTO: если веб-сервис поддерживает XDTO, используйте его для упаковки данных.
Пример для JSON:
Функция ПередатьТаблицуЧерезВебСервис(ТЗ)
ТекстJSON = ЗаписатьJSON(ТЗ);
Возврат Base64Строка(ТекстJSON);
КонецФункции
Почему после восстановления таблицы исчезли некоторые колонки?
Это происходит, если:
- Структура таблицы изменилась после записи в хранилище (например, удалили колонку).
- При записи использовался
ЗаписатьJSON(), который игнорирует пустые колонки. - В таблице были динамические колонки (добавленные через
ТЗ.Колонки.Добавить()без сохранения в метаданных).
Решение: перед записью фиксируйте структуру таблицы, а после чтения проверяйте наличие всех необходимых колонок.
Как сохранить таблицу значений в файл Excel напрямую?
Для этого не нужно использовать хранилище. В 1С 8.3 есть встроенные методы:
- Для управляемых форм:
ЭкспортВExcel(). - Для обычных форм: библиотека OneScript или COM-объект
Excel.Application.
Пример:
ТЗ.Записать("C:\temp\отчет.xlsx", ТипФайлаExcel.Excel2007);
Если нужно сохранить в хранилище и в Excel, сначала экспортируйте в Excel, а затем запишите двоичные данные файла в хранилище.
Можно ли хранить в ХранилищеЗначения данные больше 1 ГБ?
Технически да, но:
- 🛑 В 32-битных версиях 1С ограничение — ~2 ГБ на объект.
- 🛑 В 64-битных ограничение зависит от доступной памяти (до десятков ГБ).
- ⚠️ Практические ограничения:
- Сериализация больших объектов может занять несколько минут.
- В веб-клиенте передача больших данных может прерываться по тайм-ауту.
- Рекомендуемый максимум для хранилища — 100 МБ.
Для больших объёмов данных используйте:
- Разбивку на части.
- Хранение в файлах на диске (не в базе).
- Специализированные форматы (например, SQLite).