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