Как исправить ошибку «В запросе, формирующем временную таблицу, не могут выбираться вложенные таблицы» в 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке сложных отчетов или механизмов проведения документов в системе «1С:Предприятие» программисты часто используют временные таблицы для пошаговой обработки данных. Временные таблицы позволяют разбить сложный алгоритм на простые, легко читаемые и оптимизируемые блоки. Однако при попытке поместить данные табличной части какого-либо объекта во временную таблицу разработчики регулярно сталкиваются со следующей системной ошибкой:

«В запросе, формирующем временную таблицу, не могут выбираться вложенные таблицы»

В этой статье мы подробно разберем природу этого ограничения платформы 1С, выясним, почему системы управления базами данных (СУБД) не умеют работать с древовидными структурами во временных таблицах, и по шагам рассмотрим три эффективных способа обхода этой проблемы.

Почему возникает эта ошибка? Выясняем причину

Для того чтобы понять суть проблемы, нам необходимо заглянуть под капот платформы 1С и СУБД (будь то MS SQL Server, PostgreSQL или IBM DB2). В терминологии 1С «вложенная таблица» — это обращение к табличной части объекта (например, справочника или документа) непосредственно через его шапку «через точку». В коде запроса это выглядит следующим образом: Соглашения.ЭтапыГрафикаОплаты.Сдвиг.

Если мы выполняем обычный (не пакетный) запрос без помещения результатов во временную таблицу, платформа 1С берет на себя задачу по преобразованию иерархических данных. Она возвращает программисту объект типа РезультатЗапроса, в котором одна из колонок (соответствующая табличной части) сама по себе содержит другую таблицу (выборку строк табличной части).

Однако временные таблицы, создаваемые конструкцией ПОМЕСТИТЬ вт_ИмяТаблицы, физически создаются на стороне СУБД (например, в базе данных tempdb в случае MS SQL Server). Реляционные СУБД по своей природе спроектированы для работы исключительно с плоскими двумерными таблицами (строки и колонки). Они физически не способны хранить «таблицу внутри ячейки другой таблицы». Платформа 1С строго контролирует это ограничение СУБД и при попытке записать вложенную структуру во временную таблицу мгновенно блокирует выполнение запроса, выдавая ошибку.

Помимо конструкции ПОМЕСТИТЬ, вложенные таблицы также категорически запрещено использовать в следующих частях запроса:

  1. В условиях связи таблиц (секция ПО...).
  2. В условиях отбора записей (секция ГДЕ...).
  3. В группировках (секция СГРУППИРОВАТЬ ПО).
  4. В операциях объединения (секции ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ).
  5. Внутри вложенных запросов.

Теперь, когда мы детально понимаем причину возникновения ошибки, рассмотрим три практических способа её решения.

---

Способ А: Использование табличной части как основного источника запроса

Проанализируем самый простой и быстрый способ обхода ошибки. Вместо того чтобы выбирать данные из шапки справочника (например, Справочник.СоглашенияСПоставщиками) и обращаться к его табличной части через точку, мы можем сделать основным источником запроса саму табличную часть — Справочник.СоглашенияСПоставщиками.ЭтапыГрафикаОплаты.

При таком подходе реквизиты шапки (самого соглашения, его партнера, срока поставки) мы будем получать через стандартное поле Ссылка, которое присутствует в любой табличной части и указывает на родительский документ или элемент справочника.

Рассмотрим пример реализации такого запроса:


ВЫБРАТЬ
    Этапы.Ссылка КАК Соглашение,
    Этапы.Ссылка.Партнер КАК Партнер,
    Этапы.Ссылка.СрокПоставки КАК СрокПоставки,
    ВЫБОР
        КОГДА Этапы.Ссылка.ФормаОплаты = ЗНАЧЕНИЕ(Перечисление.ФормыОплаты.Наличная)
            ИЛИ Этапы.Ссылка.ФормаОплаты = ЗНАЧЕНИЕ(Перечисление.ФормыОплаты.Безналичная)
        ТОГДА Этапы.Ссылка.ФормаОплаты
        ИНАЧЕ "Любая"
    КОНЕЦ КАК ФормаОплаты,
    Этапы.Ссылка.ПорядокОплаты КАК ПорядокОплаты,
    Этапы.Сдвиг КАК Сдвиг
ПОМЕСТИТЬ вт_Соглашения
ИЗ
    Справочник.СоглашенияСПоставщиками.ЭтапыГрафикаОплаты КАК Этапы

Важный нюанс производительности: Несмотря на простоту написания, данный способ имеет существенный недостаток. Обращение к реквизитам шапки через точку от поля Ссылка (например, Этапы.Ссылка.Партнер) заставляет платформу 1С генерировать неявные левые соединения (LEFT JOIN) на уровне СУБД. Если в вашей базе данных сотни тысяч соглашений, такой запрос может работать крайне медленно. Кроме того, за использование конструкции «через две точки» (обращение к реквизиту ссылки из табличной части) гарантированно снижают баллы на экзамене «1С:Специалист по платформе».

---

Способ Б: Явное левое соединение (Стандарт разработки «1С:Совместимо»)

Разберем наиболее профессиональный и оптимизированный подход, который полностью соответствует стандартам разработки фирмы «1С». Чтобы избежать неявных соединений и не использовать вложенные таблицы, мы выполним явное соединение таблицы шапки справочника и таблицы его табличной части.

Посмотрим на пример правильного построения запроса:


ВЫБРАТЬ
    Соглашения.Ссылка КАК Соглашение,
    Соглашения.Партнер КАК Партнер,
    Соглашения.СрокПоставки КАК СрокПоставки,
    ВЫБОР
        КОГДА Соглашения.FormaOplaty = ЗНАЧЕНИЕ(Перечисление.ФормыОплаты.Наличная)
            ИЛИ Соглашения.FormaOplaty = ЗНАЧЕНИЕ(Перечисление.ФормыОплаты.Безналичная)
        ТОГДА Соглашения.FormaOplaty
        ИНАЧЕ "Любая"
    КОНЕЦ КАК ФормаОплаты,
    Соглашения.ПорядокОплаты КАК ПорядокОплаты,
    ЕСТЬNULL(Этапы.Сдвиг, 0) КАК Сдвиг
ПОМЕСТИТЬ вт_Соглашения
ИЗ
    Справочник.СоглашенияСПоставщиками КАК Соглашения
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СоглашенияСПоставщиками.ЭтапыГрафикаОплаты КАК Этапы
        ПО Этапы.Ссылка = Соглашения.Ссылка

Проанализируем, почему этот запрос работает идеально:

  1. Мы соединяем две абсолютно «плоские» таблицы: основную таблицу справочника СоглашенияСПоставщиками и таблицу его табличной части ЭтапыГрафикаОплаты.
  2. Связь устанавливается по ключевому полю Ссылка, которое проиндексировано на уровне СУБД в обеих таблицах. Это обеспечивает максимальную скорость выполнения операции соединения.
  3. Для полей табличной части мы используем функцию ЕСТЬNULL. Это позволяет избежать появления пустых значений (NULL) в результирующей временной таблице, если у соглашения вообще не заполнены этапы оплаты.

Этот способ является универсальным стандартом для работы с записанными в базу данных объектами.

---

Способ В: Обход через Таблицу Значений (для незаписанных объектов)

Иногда перед разработчиком стоит задача обработать данные табличной части документа или справочника, которые пользователь только что ввел на форме, но еще не успел записать в базу данных. В этой ситуации напрямую обратиться запросом к таблицам СУБД невозможно, так как этих данных там физически еще нет.

В таком случае мы можем использовать механизм передачи внешней таблицы значений в качестве параметра запроса с последующим её помещением во временную таблицу. Разберем этот процесс по шагам.

Шаг 1. Выгрузка данных на сервере

Сначала мы выгружаем табличную часть объекта в обычную Таблицу Значений встроенного языка 1С:

ТаблицаДанных = Объект.ЭтапыГрафикаОплаты.Выгрузить();

Важное предупреждение: Платформа 1С не сможет поместить Таблицу Значений во временную таблицу запроса, если у колонок Таблицы Значений не будут жестко определены типы данных (объект ОписаниеТипов). При обычной выгрузке из ТЧ типы колонок переносятся автоматически, но если вы создаете Таблицу Значений вручную в коде, обязательно задавайте типы явно.

Шаг 2. Передача таблицы в запрос и инициализация Менеджера Временных Таблиц

Создаем запрос, инициализируем свойство МенеджерВременныхТаблиц и передаем нашу таблицу в качестве параметра:


Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

// Описываем запрос получения данных из внешнего источника
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ВнешнийИсточник.Сдвиг КАК Сдвиг,
    |    ВнешнийИсточник.ВариантОплаты КАК ВариантОплаты
    |ПОМЕСТИТЬ вт_ЭтапыГрафика
    |ИЗ
    |    &ТаблицаДанных КАК ВнешнийИсточник";

Запрос.УстановитьПараметр("ТаблицаДанных", ТаблицаДанных);
Запрос.Выполнить();

Теперь временная таблица вт_ЭтапыГрафика успешно создана в оперативной памяти СУБД и доступна для дальнейшего соединения с любыми физическими таблицами базы данных в рамках этого же сеанса запроса.

---

Подведем итоги

Мы выяснили, что ошибка вложенных таблиц во временных таблицах — это логичное и непреодолимое ограничение реляционных баз данных. Для решения этой проблемы мы рассмотрели три надежных инструмента:

Используя явное левое соединение (Способ Б), вы гарантируете быструю работу ваших алгоритмов даже при обработке огромных массивов информации.

← На главную