При разработке сложных отчетов или механизмов проведения документов в системе «1С:Предприятие» программисты часто используют временные таблицы для пошаговой обработки данных. Временные таблицы позволяют разбить сложный алгоритм на простые, легко читаемые и оптимизируемые блоки. Однако при попытке поместить данные табличной части какого-либо объекта во временную таблицу разработчики регулярно сталкиваются со следующей системной ошибкой:
«В запросе, формирующем временную таблицу, не могут выбираться вложенные таблицы»
В этой статье мы подробно разберем природу этого ограничения платформы 1С, выясним, почему системы управления базами данных (СУБД) не умеют работать с древовидными структурами во временных таблицах, и по шагам рассмотрим три эффективных способа обхода этой проблемы.
Для того чтобы понять суть проблемы, нам необходимо заглянуть под капот платформы 1С и СУБД (будь то MS SQL Server, PostgreSQL или IBM DB2). В терминологии 1С «вложенная таблица» — это обращение к табличной части объекта (например, справочника или документа) непосредственно через его шапку «через точку». В коде запроса это выглядит следующим образом: Соглашения.ЭтапыГрафикаОплаты.Сдвиг.
Если мы выполняем обычный (не пакетный) запрос без помещения результатов во временную таблицу, платформа 1С берет на себя задачу по преобразованию иерархических данных. Она возвращает программисту объект типа РезультатЗапроса, в котором одна из колонок (соответствующая табличной части) сама по себе содержит другую таблицу (выборку строк табличной части).
Однако временные таблицы, создаваемые конструкцией ПОМЕСТИТЬ вт_ИмяТаблицы, физически создаются на стороне СУБД (например, в базе данных tempdb в случае MS SQL Server). Реляционные СУБД по своей природе спроектированы для работы исключительно с плоскими двумерными таблицами (строки и колонки). Они физически не способны хранить «таблицу внутри ячейки другой таблицы». Платформа 1С строго контролирует это ограничение СУБД и при попытке записать вложенную структуру во временную таблицу мгновенно блокирует выполнение запроса, выдавая ошибку.
Помимо конструкции ПОМЕСТИТЬ, вложенные таблицы также категорически запрещено использовать в следующих частях запроса:
ПО...).ГДЕ...).СГРУППИРОВАТЬ ПО).ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ).Теперь, когда мы детально понимаем причину возникновения ошибки, рассмотрим три практических способа её решения.
---
Проанализируем самый простой и быстрый способ обхода ошибки. Вместо того чтобы выбирать данные из шапки справочника (например, Справочник.СоглашенияСПоставщиками) и обращаться к его табличной части через точку, мы можем сделать основным источником запроса саму табличную часть — Справочник.СоглашенияСПоставщиками.ЭтапыГрафикаОплаты.
При таком подходе реквизиты шапки (самого соглашения, его партнера, срока поставки) мы будем получать через стандартное поле Ссылка, которое присутствует в любой табличной части и указывает на родительский документ или элемент справочника.
Рассмотрим пример реализации такого запроса:
ВЫБРАТЬ
Этапы.Ссылка КАК Соглашение,
Этапы.Ссылка.Партнер КАК Партнер,
Этапы.Ссылка.СрокПоставки КАК СрокПоставки,
ВЫБОР
КОГДА Этапы.Ссылка.ФормаОплаты = ЗНАЧЕНИЕ(Перечисление.ФормыОплаты.Наличная)
ИЛИ Этапы.Ссылка.ФормаОплаты = ЗНАЧЕНИЕ(Перечисление.ФормыОплаты.Безналичная)
ТОГДА Этапы.Ссылка.ФормаОплаты
ИНАЧЕ "Любая"
КОНЕЦ КАК ФормаОплаты,
Этапы.Ссылка.ПорядокОплаты КАК ПорядокОплаты,
Этапы.Сдвиг КАК Сдвиг
ПОМЕСТИТЬ вт_Соглашения
ИЗ
Справочник.СоглашенияСПоставщиками.ЭтапыГрафикаОплаты КАК Этапы
Важный нюанс производительности: Несмотря на простоту написания, данный способ имеет существенный недостаток. Обращение к реквизитам шапки через точку от поля Ссылка (например, Этапы.Ссылка.Партнер) заставляет платформу 1С генерировать неявные левые соединения (LEFT JOIN) на уровне СУБД. Если в вашей базе данных сотни тысяч соглашений, такой запрос может работать крайне медленно. Кроме того, за использование конструкции «через две точки» (обращение к реквизиту ссылки из табличной части) гарантированно снижают баллы на экзамене «1С:Специалист по платформе».
---
Разберем наиболее профессиональный и оптимизированный подход, который полностью соответствует стандартам разработки фирмы «1С». Чтобы избежать неявных соединений и не использовать вложенные таблицы, мы выполним явное соединение таблицы шапки справочника и таблицы его табличной части.
Посмотрим на пример правильного построения запроса:
ВЫБРАТЬ
Соглашения.Ссылка КАК Соглашение,
Соглашения.Партнер КАК Партнер,
Соглашения.СрокПоставки КАК СрокПоставки,
ВЫБОР
КОГДА Соглашения.FormaOplaty = ЗНАЧЕНИЕ(Перечисление.ФормыОплаты.Наличная)
ИЛИ Соглашения.FormaOplaty = ЗНАЧЕНИЕ(Перечисление.ФормыОплаты.Безналичная)
ТОГДА Соглашения.FormaOplaty
ИНАЧЕ "Любая"
КОНЕЦ КАК ФормаОплаты,
Соглашения.ПорядокОплаты КАК ПорядокОплаты,
ЕСТЬNULL(Этапы.Сдвиг, 0) КАК Сдвиг
ПОМЕСТИТЬ вт_Соглашения
ИЗ
Справочник.СоглашенияСПоставщиками КАК Соглашения
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СоглашенияСПоставщиками.ЭтапыГрафикаОплаты КАК Этапы
ПО Этапы.Ссылка = Соглашения.Ссылка
Проанализируем, почему этот запрос работает идеально:
СоглашенияСПоставщиками и таблицу его табличной части ЭтапыГрафикаОплаты.Ссылка, которое проиндексировано на уровне СУБД в обеих таблицах. Это обеспечивает максимальную скорость выполнения операции соединения.ЕСТЬNULL. Это позволяет избежать появления пустых значений (NULL) в результирующей временной таблице, если у соглашения вообще не заполнены этапы оплаты.Этот способ является универсальным стандартом для работы с записанными в базу данных объектами.
---
Иногда перед разработчиком стоит задача обработать данные табличной части документа или справочника, которые пользователь только что ввел на форме, но еще не успел записать в базу данных. В этой ситуации напрямую обратиться запросом к таблицам СУБД невозможно, так как этих данных там физически еще нет.
В таком случае мы можем использовать механизм передачи внешней таблицы значений в качестве параметра запроса с последующим её помещением во временную таблицу. Разберем этот процесс по шагам.
Шаг 1. Выгрузка данных на сервере
Сначала мы выгружаем табличную часть объекта в обычную Таблицу Значений встроенного языка 1С:
ТаблицаДанных = Объект.ЭтапыГрафикаОплаты.Выгрузить();
Важное предупреждение: Платформа 1С не сможет поместить Таблицу Значений во временную таблицу запроса, если у колонок Таблицы Значений не будут жестко определены типы данных (объект ОписаниеТипов). При обычной выгрузке из ТЧ типы колонок переносятся автоматически, но если вы создаете Таблицу Значений вручную в коде, обязательно задавайте типы явно.
Шаг 2. Передача таблицы в запрос и инициализация Менеджера Временных Таблиц
Создаем запрос, инициализируем свойство МенеджерВременныхТаблиц и передаем нашу таблицу в качестве параметра:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
// Описываем запрос получения данных из внешнего источника
Запрос.Текст =
"ВЫБРАТЬ
| ВнешнийИсточник.Сдвиг КАК Сдвиг,
| ВнешнийИсточник.ВариантОплаты КАК ВариантОплаты
|ПОМЕСТИТЬ вт_ЭтапыГрафика
|ИЗ
| &ТаблицаДанных КАК ВнешнийИсточник";
Запрос.УстановитьПараметр("ТаблицаДанных", ТаблицаДанных);
Запрос.Выполнить();
Теперь временная таблица вт_ЭтапыГрафика успешно создана в оперативной памяти СУБД и доступна для дальнейшего соединения с любыми физическими таблицами базы данных в рамках этого же сеанса запроса.
---
Мы выяснили, что ошибка вложенных таблиц во временных таблицах — это логичное и непреодолимое ограничение реляционных баз данных. Для решения этой проблемы мы рассмотрели три надежных инструмента:
Используя явное левое соединение (Способ Б), вы гарантируете быструю работу ваших алгоритмов даже при обработке огромных массивов информации.