Как исправить ошибку «Содержимое объекта данных может быть выбрано только во временную таблицу» в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

Выясним причину возникновения ошибки

Чтобы понять природу этого поведения, рассмотрим подробнее, как работает платформа «1С:Предприятие» на физическом уровне. Платформа построена по трехзвенной архитектуре: клиент, сервер приложений 1С и сервер баз данных (СУБД — MS SQL Server, PostgreSQL, IBM DB2 или Oracle).

Когда мы создаем объект типа ТаблицаЗначений в коде, эти данные хранятся исключительно в оперативной памяти сервера приложений 1С. Сама СУБД ничего не знает о существовании этой таблицы. Для того чтобы СУБД могла выполнить по ней запрос (например, соединить с физической таблицей справочника или документа в базе данных), эти данные необходимо физически перенести на сторону сервера СУБД — то есть создать реальную временную таблицу в системной базе данных (например, в tempdb для MS SQL Server) и заполнить её строками.

Транслятор запросов 1С умеет выполнять эту операцию переноса данных из оперативной памяти в базу данных только с помощью максимально простой, изолированной инструкции:

ВЫБРАТЬ Поля ПОМЕСТИТЬ ВременнаяТаблица ИЗ &ВнешнийИсточник

СУБД не способна «на лету» выполнять объединения (UNION), соединения (JOIN) или группировки над данными, которых еще физически нет в базе данных. Именно поэтому платформа накладывает жесткое ограничение: любой внешний источник при первом обращении к нему в запросе должен быть безоговорочно и изолированно помещен во временную таблицу. Любые другие операции в этом же пакете запроса запрещены.

Проанализируем ситуацию: нерабочий пример кода

Посмотрим на пример, с которым столкнулся автор темы на форуме. Он попытался сразу объединить данные двух параметров (&Товары и &Работы) и поместить результат в одну временную таблицу:


Запрос = Новый Запрос("
|ВЫБРАТЬ
|    Товары.Номенклатура КАК Номенклатура,
|    Товары.СуммаСНДС КАК СуммаСНДС,
|    Товары.Отменено КАК Отменено
|ПОМЕСТИТЬ
|    Товары
|ИЗ
|    &Товары КАК Товары
|Объединить все    
|ВЫБРАТЬ
|    Работы.Номенклатура ,
|    Работы.СуммаСНДС ,
|    Работы.Отменено 
|ИЗ
|    &Работы КАК Работы
|;
|... дальнейший текст запроса ...
|")

Этот запрос гарантированно завершится аварийно. Платформа анализирует первый пакет запроса и видит, что разработчик пытается выполнить операцию ОБЪЕДИНИТЬ ВСЕ непосредственно для двух внешних источников &Товары и &Работы еще до того, как они были записаны в базу данных. Транслятор запросов выдает ошибку, так как правила работы с внешними объектами были грубо нарушены.

Основные правила и ограничения при работе с внешними источниками в 1С

При передаче таблиц значений или табличных частей в качестве параметров запроса необходимо строго соблюдать следующие правила:

  1. Запрет на любые операции при первом обращении. В пакете запроса, который выбирает данные из параметра (например, ИЗ &ТаблицаПараметр), категорически запрещено использовать:
    • Объединения (ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ).
    • Любые виды соединений (ЛЕВОЕ СОЕДИНЕНИЕ, ВНУТРЕННЕЕ и т. д.).
    • Группировки (СГРУППИРОВАТЬ ПО) и агрегатные функции (СУММА, КОЛИЧЕСТВО, МАКСИМУМ).
    • Условия связи и вложенные запросы.
  2. Запрет на обращение к реквизитам через точку. Вы не можете написать Товары.Номенклатура.ТипНоменклатуры при первой выборке из внешнего источника. Обращение через точку платформа неявно преобразует в левое соединение с соответствующей таблицей справочника БД. Соединения на данном этапе запрещены. Сначала нужно поместить ссылку во временную таблицу, а на следующем шаге уже обращаться к ее реквизитам.
  3. Обязательная типизация колонок таблицы значений. Если вы создаете ТаблицаЗначений программно в коде, обязательно задавайте жесткие типы колонок через объект ОписаниеТипов. В противном случае СУБД не сможет определить типы полей для создания временной таблицы, и вы получите ошибку: «Тип не может быть выбран в запросе».

Разберем по шагам правильное решение проблемы (Best Practice)

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

Посмотрим на пример правильной реализации запроса, который полностью решает проблему автора:


// Создаем менеджер временных таблиц, чтобы связать пакеты запроса
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

Запрос.Текст = 
"ВЫБРАТЬ
|    Товары.Номенклатура КАК Номенклатура,
|    Товары.СуммаСНДС КАК СуммаСНДС,
|    Товары.Отменено КАК Отменено
|ПОМЕСТИТЬ ВТ_ТоварыПараметр
|ИЗ
|    &Товары КАК Товары
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    Работы.Номенклатура КАК Номенклатура,
|    Работы.СуммаСНДС КАК СуммаСНДС,
|    Работы.Отменено КАК Отменено
|ПОМЕСТИТЬ ВТ_РаботыПараметр
|ИЗ
|    &Работы КАК Работы
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    ВТ_ТоварыПараметр.Номенклатура КАК Номенклатура,
|    ВТ_ТоварыПараметр.СуммаСНДС КАК СуммаСНДС,
|    ВТ_ТоварыПараметр.Отменено КАК Отменено
|ПОМЕСТИТЬ ВТ_ВсеОбъекты
|ИЗ
|    ВТ_ТоварыПараметр КАК ВТ_ТоварыПараметр
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|    ВТ_РаботыПараметр.Номенклатура,
|    ВТ_РаботыПараметр.СуммаСНДС,
|    ВТ_РаботыПараметр.Отменено
|ИЗ
|    ВТ_РаботыПараметр КАК ВТ_РаботыПараметр
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    ЕСТЬNULL(СУММА(ВТ_ВсеОбъекты.СуммаСНДС), 0) КАК СуммаСНДС
|ИЗ
|    ВТ_ВсеОбъекты КАК ВТ_ВсеОбъекты
|ГДЕ
|    НЕ ВТ_ВсеОбъекты.Отменено
|    И (((ВТ_ВсеОбъекты.Номенклатура.ТипНоменклатуры <> ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.МногооборотнаяТара)
|            ИЛИ (НЕ &ВернутьМногооборотнуюТару) ИЛИ &ТребуетсяЗалогЗаТару)
|            И НЕ &ТолькоЗалогЗаТару)
|        ИЛИ (ВТ_ВсеОбъекты.Номенклатура.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.МногооборотнаяТара)
|            И &ВернутьМногооборотнуюТару
|            И &ТребуетсяЗалогЗаТару
|            И &ТолькоЗалогЗаТару))";

// Установка параметров и выполнение запроса
Запрос.УстановитьПараметр("Товары", ТаблицаТоваров);
Запрос.УстановитьПараметр("Работы", ТаблицаРабот);
// Установите остальные параметры запроса...

РезультатЗапроса = Запрос.Выполнить();

Что изменилось в этом решении? Мы создали две независимые временные таблицы: ВТ_ТоварыПараметр и ВТ_РаботыПараметр. СУБД успешно создала их физические копии в базе данных. Третьим пакетом мы спокойно выполнили операцию ОБЪЕДИНИТЬ ВСЕ над уже существующими временными таблицами и сохранили общий результат в ВТ_ВсеОбъекты. И, наконец, в четвертом пакете мы без проблем обратились к реквизиту через точку (ВТ_ВсеОбъекты.Номенклатура.ТипНоменклатуры) и выполнили агрегатную функцию СУММА с группировкой.

Использование этого шаблона проектирования гарантирует высокую производительность запросов, легкую отладку в консоли запросов и полное отсутствие подобных ошибок СУБД в ваших разработках.

← На главную