При разработке конфигураций на платформе 1С:Предприятие 8.3 (начиная с версии 8.3.11) программисты часто сталкиваются с необходимостью оптимизации хранения файлов и тяжелых бинарных данных. Механизм Хранилища двоичных данных (ХДД) позволяет выносить содержимое реквизитов типа ХранилищеЗначения за пределы основных таблиц базы данных (в том числе в S3-совместимые хранилища). Однако при попытке включить эту опцию для реквизитов табличных частей многие обнаруживают, что соответствующий переключатель в конфигураторе отсутствует. Разберем подробно, почему так происходит и как правильно решить задачу хранения файлов, привязанных к строкам табличной части.
Проанализируем текущую реализацию платформы (включая последние релизы, такие как 8.3.27). На данный момент использование механизма «Использование хранения в хранилище двоичных данных» официально поддерживается только для реквизитов верхнего уровня некоторых объектов:
Для реквизитов табличных частей (ТЧ) такая возможность не предусмотрена. Выясним причину этой «жестокости» платформы. Механизм ХДД жестко завязан на уникальную идентификацию записи в базе данных. В случае со справочником или документом — это ссылка (Ref). В случае регистра сведений — это набор измерений. В табличных частях идентификация конкретной строки сложнее: строки не имеют постоянного уникального идентификатора, который был бы доступен платформе для автоматического управления жизненным циклом связанного файла (например, для автоматического удаления файла при удалении строки ТЧ).
Рассмотрим ситуацию, когда разработчик все же решает использовать обычный тип ХранилищеЗначения в реквизитах ТЧ. Проанализируем последствия для производительности СУБД:
rphost.Поскольку для ресурсов регистров сведений механизм ХДД доступен и стабильно работает, мы можем реализовать обходное решение. Рассмотрим по шагам, как организовать хранение двоичных данных для строк табличной части:
Шаг 1: Подготовка табличной части
В табличной части вашего объекта (справочника или документа) вместо реквизита с типом ХранилищеЗначения создадим реквизит ИдентификаторСтроки с типом УникальныйИдентификатор или используем ссылку на вспомогательный справочник. Это позволит нам однозначно связать строку ТЧ с данными в другом месте.
Шаг 2: Создание регистра сведений
Создадим непериодический независимый регистр сведений (например, ДанныеФайловТЧ). Настроим его структуру следующим образом:
ИдентификаторСтроки (тип тот же, что и в ТЧ).Данные (тип ХранилищеЗначения).Шаг 3: Включение Хранилища двоичных данных
В свойствах ресурса Данные нашего регистра найдем категорию «Использование» и установим значение «Использование хранения в хранилище двоичных данных» в положение «Использовать».
Посмотрим на пример кода для сохранения данных из строки табличной части в наш регистр:
// При записи объекта или строки ТЧ
Процедура СохранитьДанныеСтроки(СтрокаТЧ, ДвоичныеДанные)
// Если идентификатор еще не присвоен - генерируем его
Если СтрокаТЧ.ИдентификаторСтроки.Пустая() Тогда
СтрокаТЧ.ИдентификаторСтроки = Новый УникальныйИдентификатор();
КонецЕсли;
МенеджерЗаписи = РегистрыСведений.ДанныеФайловТЧ.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИдентификаторСтроки = СтрокаТЧ.ИдентификаторСтроки;
МенеджерЗаписи.Данные = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
МенеджерЗаписи.Записать();
КонецПроцедуры
Разберем, как получить данные обратно (возможно, потребуется дополнительное сжатие) для отображения или обработки:
Функция ПолучитьДанныеСтроки(ИдентификаторСтроки)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеФайловТЧ.Данные КАК Данные
|ИЗ
| РегистрСведений.ДанныеФайловТЧ КАК ДанныеФайловТЧ
|ГДЕ
| ДанныеФайловТЧ.ИдентификаторСтроки = &ИдентификаторСтроки";
Запрос.УстановитьПараметр("ИдентификаторСтроки", ИдентификаторСтроки);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Возврат Выборка.Данные.Получить();
КонецЕсли;
Возврат Неопределено;
КонецФункции
Существует мнение, что 1С всегда считывает бинарные данные при вызове ПолучитьОбъект(). Проанализируем, так ли это на самом деле. На техническом уровне в платформе реализована «ленивая загрузка» (lazy loading):
Ссылка.ПолучитьОбъект() платформа считывает только простые типы (числа, строки, даты) и ссылки.ХранилищеЗначения в памяти создается только «контейнер».Хранилище.Получить().Тем не менее, даже при наличии ленивой загрузки, использование регистров с ХДД является более предпочтительным. Это связано с тем, что механизм ХДД позволяет использовать внешние хранилища (S3), что снимает нагрузку с сервера приложений 1С, так как данные могут передаваться клиенту эффективнее, минуя лишние преобразования внутри rphost.
Резюме: Если вам требуется использовать хранилище двоичных данных для элементов табличной части, используйте архитектурный паттерн с выносом данных в Регистр сведений. Это обеспечит высокую производительность системы, чистоту основной базы данных и позволит использовать все современные возможности платформы 8.3 по работе с внешними хранилищами.