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