При разработке отчетов и обработок в 1С часто возникает задача получить список объектов вместе с их характеристиками или свойствами. Разработчики нередко пытаются оптимизировать код и структуру данных, желая получить в результате запроса таблицу значений, в одной из колонок которой будет содержаться вложенная таблица со всеми свойствами конкретной позиции номенклатуры. Разберем, возможно ли реализовать такое поведение на уровне языка запросов 1С, с какими ограничениями платформы придется столкнуться и какие альтернативные подходы существуют.
Посмотрим на синтаксис, который разработчики часто пытаются применить по аналогии с табличными частями документов или справочников:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
ЗначенияСвойствОбъектов.(Свойство, Значение) КАК СвойстваНоменклатуры
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ПО Номенклатура.Ссылка = ЗначенияСвойствОбъектов.Объект
При выполнении такого запроса платформа выдаст синтаксическую ошибку. Выясним причину такого поведения системы. Конструкция вида ИмяТаблицы.(Поле1, Поле2) доступна исключительно для подчиненных таблиц — табличных частей прикладных объектов (справочников, документов, отчетов и др.).
На уровне базы данных табличная часть жестко привязана к ссылке родительского объекта через внутреннее поле Ссылка. Независимый регистр сведений (такой как РегистрСведений.ЗначенияСвойствОбъектов) не является встроенной табличной частью справочника. Он представляет собой самостоятельную сущность СУБД, связь с которой задается динамически через условие соединения. По этой причине преобразовать строки регистра сведений во вложенную таблицу непосредственно в блоке ВЫБРАТЬ невозможно.
Проанализируем конфигурации, разработанные на базе Библиотеки Стандартных Подсистем (БСП). В них свойства объектов разделены на две категории:
РегистрСведений.ДополнительныеСведения. К ним нельзя применить синтаксис вложенных таблиц.Справочник.Номенклатура.ДополнительныеРеквизиты).Если в вашей системе свойства номенклатуры настроены именно как дополнительные реквизиты, мы можем получить вложенную таблицу прямым обращением к табличной части:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
Номенклатура.ДополнительныеРеквизиты.(
Свойство КАК Свойство,
Значение КАК Значение
) КАК СвойстваНоменклатуры
ИЗ
Справочник.Номенклатура КАК Номенклатура
При выгрузке результата такого запроса методом Запрос.Выполнить().Выгрузить() в колонке СвойстваНоменклатуры действительно будет находиться отдельная таблица значений для каждой строки номенклатуры.
Важное предостережение: если вы планируете передавать полученную таблицу значений обратно в запрос как внешнюю таблицу (конструкция ПОМЕСТИТЬ ВТ Из &ВнешняяТЗ), платформа 1С вызовет исключение. Временные таблицы не поддерживают колонки с составными типами и вложенными коллекциями (таблицами значений).
Если данные требуются для дальнейших расчетов внутри одного пакетного запроса, наиболее производительным и архитектурно правильным решением будет отказ от вложенных структур в пользу плоской временной таблицы с индексацией:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
ЗначенияСвойствОбъектов.Свойство КАК Свойство,
ЗначенияСвойствОбъектов.Значение КАК Значение
ПОМЕСТИТЬ ВТ_СвойстваНоменклатуры
ИЗ
Справочник.Номенклатура КАК Номенклатура
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ПО Номенклатура.Ссылка = ЗначенияСвойствОбъектов.Объект
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
Свойство;
Такой подход обеспечивает максимальную скорость работы СУБД. В последующих запросах пакета к временной таблице ВТ_СвойстваНоменклатуры можно легко обращаться через левые соединения по полям Номенклатура и Свойство.
Если вложенные таблицы значений требуются исключительно для обработки алгоритмами на встроенном языке 1С, соберем структуру вручную. Чтобы избежать медленного выполнения в циклах, применим метод НайтиСтроки() с предварительным индексированием колонок.
Разберем по шагам реализацию данного алгоритма:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Номенклатура.Ссылка КАК Номенклатура
|ИЗ
| Справочник.Номенклатура КАК Номенклатура;
|
|ВЫБРАТЬ
| ЗначенияСвойствОбъектов.Объект КАК Номенклатура,
| ЗначенияСвойствОбъектов.Свойство КАК Свойство,
| ЗначенияСвойствОбъектов.Значение КАК Значение
|ИЗ
| РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов";
РезультатыЗапроса = Запрос.ВыполнитьПакет();
ТЗ_Номенклатура = РезультатыЗапроса[0].Выгрузить();
ТЗ_Свойства = РезультатыЗапроса[1].Выгрузить();
// Индексируем колонку для быстрого поиска строк методом НайтиСтроки
ТЗ_Свойства.Индексы.Добавить("Номенклатура");
// Подготавливаем результирующую таблицу
ТЗ_Результат = Новый ТаблицаЗначений;
ТЗ_Результат.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ_Результат.Колонки.Добавить("СвойстваНоменклатуры", Новый ОписаниеТипов("ТаблицаЗначений"));
Отбор = Новый Структура("Номенклатура");
Для Каждого СтрНом Из ТЗ_Номенклатура Цикл
НоваяСтрока = ТЗ_Результат.Добавить();
НоваяСтрока.Номенклатура = СтрНом.Номенклатура;
// Создаем структуру вложенной таблицы значений
ТЗ_Вложенная = Новый ТаблицаЗначений;
ТЗ_Вложенная.Колонки.Добавить("Свойство");
ТЗ_Вложенная.Колонки.Добавить("Значение");
Отбор.Номенклатура = СтрНом.Номенклатура;
НайденныеСтроки = ТЗ_Свойства.НайтиСтроки(Отбор);
Для Каждого СтрСвойство Из НайденныеСтроки Цикл
ЗаполнитьЗначенияСвойств(ТЗ_Вложенная.Добавить(), СтрСвойство);
КонецЦикла;
НоваяСтрока.СвойстваНоменклатуры = ТЗ_Вложенная;
КонецЦикла;
Данный способ работает значительно быстрее последовательного выполнения запросов в цикле и дает именно ту структуру данных, которая необходима для дальнейшей логики обработки.