В процессе разработки в среде «1С:Предприятие» многие начинающие (и не только) специалисты сталкиваются с архитектурным вопросом: почему в отчете или обработке мы можем легко создать реквизит с типом СписокЗначений, а в документе или справочнике платформа этого сделать не позволяет? На первый взгляд это кажется досадным ограничением, ведь список значений — это крайне удобный инструмент для выбора нескольких элементов. Однако за этим решением кроются фундаментальные принципы работы баз данных.
В этой статье мы подробно разберем, почему архитектура 1С устроена именно так, проанализируем разницу между структурами данных и рассмотрим несколько эффективных способов реализации аналогичного функционала в объектах метаданных.
Разберем ситуацию с точки зрения хранения данных. Когда мы создаем реквизит у документа, платформа 1С должна зарезервировать для него место в физической таблице базы данных (MS SQL, PostgreSQL или встроенный файловый движок). Каждый обычный реквизит объекта — это одна колонка в строке таблицы. Эта колонка должна иметь строго определенный тип данных: число, дата, строка фиксированной или неограниченной длины, либо ссылка (GUID).
Проанализируем структуру объекта СписокЗначений. Это не просто массив значений, это сложная коллекция, где каждый элемент состоит из:
Если бы платформа позволила добавить такой тип в реквизит, ей пришлось бы либо нарушить правила нормализации баз данных (записывая сложную структуру в одну ячейку в нечитаемом виде), либо создавать скрытые таблицы для каждого такого реквизита. Поскольку документы предназначены для быстрого поиска, фильтрации и проведения по регистрам, использование таких «тяжелых» структур в одной колонке сделало бы невозможным выполнение эффективных SQL-запросов к этим данным.
В отчетах же и обработках реквизиты чаще всего хранятся только в оперативной памяти в течение сеанса работы пользователя или сериализуются целиком в системные настройки, где скорость поиска по отдельным полям списка не критична. Именно поэтому там это разрешено.
Часто в обсуждениях возникает мнение, что СписокЗначений — это та же табличная часть, только с одной колонкой. Давайте выясним причину, почему это не совсем так. Рассмотрим основные отличия:
НомерСтроки и связь по ссылке с владельцем. Это позволяет быстро выбирать данные запросом.Если вам критически важно сохранить именно объект СписокЗначений целиком, и при этом вы не планируете использовать данные этого списка в текстах запросов (например, для фильтрации в отчетах), можно воспользоваться типом ХранилищеЗначений. Рассмотрим, как это реализовать программно.
Сначала создадим в документе реквизит, например, ДополнительныеПараметры, с типом ХранилищеЗначений. Для записи данных используем следующий код:
// Пример записи Списка значений в реквизит объекта
СписокДляЗаписи = Новый СписокЗначений;
СписокДляЗаписи.Добавить("Параметр1", "Первое значение");
СписокДляЗаписи.Добавить(100, "Числовой параметр");
ОбъектДокумент = РеквизитФормыВЗначение("Объект");
ОбъектДокумент.ДополнительныеПараметры = Новый ХранилищеЗначения(СписокДляЗаписи);
ОбъектДокумент.Записать();
Для восстановления данных при открытии формы выполним обратное преобразование:
// Пример извлечения данных из Хранилища
ДанныеХранилища = Объект.ДополнительныеПараметры.Получить();
Если ТипЗнч(ДанныеХранилища) = Тип("СписокЗначений") Тогда
МойСписокНаФорме = ДанныеХранилища;
КонецЕсли;
Важно: Помните, что данные внутри ХранилищаЗначения сжаты и хранятся в бинарном виде. Вы не сможете написать запрос типа ГДЕ ДополнительныеПараметры СОДЕРЖИТ "Параметр1". Это решение подходит только для «технического» хранения данных.
Более современный и «прозрачный» метод — преобразование списка в строку формата JSON. Это удобнее тем, что при отладке или просмотре базы через консоль запросов вы увидите текст, а не бинарные данные. Для этого реквизит должен иметь тип Строка (неограниченной длины).
Разберем по шагам процесс сериализации:
// Превращаем список значений в строку JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
СериализаторXDTO.ЗаписатьJSON(ЗаписьJSON, МойСписокЗначений);
СтрокаДляХранения = ЗаписьJSON.Закрыть();
// Теперь записываем СтрокаДляХранения в обычный реквизит типа Строка
Для обратного получения объекта используем чтение JSON. Этот метод также не позволяет полноценно использовать данные в запросах, но он гораздо легче в отладке.
Если вам нужно, чтобы данные из списка были доступны в отчетах, необходимо использовать Табличную часть. Это самый правильный путь с точки зрения производительности и архитектуры 1С. Чтобы интерфейсно это выглядело как список значений, вы можете просто скрыть лишние кнопки командной панели и оставить только одну колонку Значение.
Проанализируем преимущества этого подхода:
Бывают ситуации, когда список нужен только для удобства пользователя во время редактирования, а в базе должны оказаться только результаты его выбора. В этом случае мы создаем реквизит с типом СписокЗначений непосредственно в Форме документа (не в самом объекте!).
Рассмотрим алгоритм работы:
ПриЧтенииНаСервере мы заполняем этот список из данных документа (например, из строк табличной части или разделенной строки).ПередЗаписьюНаСервере мы программно переносим данные из экранного списка обратно в структуру объекта (в ТЧ или строку).Мы выяснили, что отсутствие типа СписокЗначений в реквизитах объектов — это не программная ошибка, а следствие строгой структуры реляционных баз данных. Для решения задачи хранения множественных значений мы рекомендуем: