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