Как обойти ошибку «Нельзя выбрать данные ТЧ во временной таблице» в запросах 1С?

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

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

Выясняем причину: почему СУБД и платформа накладывают это ограничение?

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

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

Способ 1. Прямое обращение к табличной части (Развертывание)

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

Давайте посмотрим на конкретный пример. Предположим, у нас есть следующий нерабочий запрос:


ВЫБРАТЬ
    Акт.Ссылка КАК Ссылка,
    Акт.ПоДаннымОрганизации.(
        Номенклатура,
        Сумма
    ) КАК ТабличнаяЧасть
ПОМЕСТИТЬ ВТ_ДанныеАкта
ИЗ
    Документ.АктСверкиВзаиморасчетов КАК Акт
ГДЕ
    Акт.Ссылка = &Ссылка

Этот запрос вызовет ошибку компиляции. Чтобы исправить его, мы должны переписать обращение напрямую к табличной части ПоДаннымОрганизации этого документа. Измененный и абсолютно корректный запрос будет выглядеть следующим образом:


ВЫБРАТЬ
    ТЧ.Ссылка КАК Ссылка,
    ТЧ.Номенклатура КАК Номенклатура,
    ТЧ.Сумма КАК Сумма
ПОМЕСТИТЬ ВТ_ДанныеАкта
ИЗ
    Документ.АктСверкиВзаиморасчетов.ПоДаннымОрганизации КАК ТЧ
ГДЕ
    ТЧ.Ссылка = &Ссылка

В данном случае мы выбираем плоские строки из физической таблицы Документ.АктСверкиВзаиморасчетов.ПоДаннымОрганизации. Каждая строка содержит ссылку на документ-владелец и простые типы данных в колонках. СУБД легко создает такую временную таблицу, и ошибка больше не возникает.

Способ 2. Использование Таблицы Значений (ТЗ) как параметра запроса

Что делать, если документ, табличную часть которого нам нужно обработать во временной таблице, еще не записан в базу данных? Например, пользователь только что заполнил форму на клиенте, нажал кнопку «Рассчитать», и нам требуется произвести сложные вычисления в запросе без предварительного сохранения объекта в БД.

В этой ситуации прямое обращение к таблице Документ...ТабличнаяЧасть не вернет новые данные, так как в базе данных их еще просто нет. Решением является выгрузка табличной части в Таблицу Значений на сервере и последующая передача этой таблицы в запрос в качестве параметра.

Рассмотрим важный нюанс, о котором часто забывают начинающие разработчики: колонки передаваемой Таблицы Значений должны быть строго типизированы. Если мы выгружаем ТЧ стандартным методом Выгрузить(), типы колонок наследуются из метаданных документа автоматически. Но если мы создаем или изменяем ТЗ вручную в коде, мы обязаны явно задать ОписаниеТипов для каждой колонки. Иначе платформа выдаст другую ошибку: «Тип не может быть выбран в запросе».

Разберем по шагам пример реализации этого подхода в коде:


// Шаг 1. Получаем таблицу значений из табличной части незаписанного документа
ТаблицаДанных = Объект.ПоДаннымОрганизации.Выгрузить();

// Шаг 2. Создаем запрос и менеджер временных таблиц
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

// Шаг 3. Передаем таблицу значений как параметр запроса
Запрос.УстановитьПараметр("ВнешнийИсточникТЧ", ТаблицаДанных);

// Шаг 4. Помещаем данные во временную таблицу и производим дальнейшие расчеты
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ВнешнийИсточник.Номенклатура КАК Номенклатура,
    |   ВнешнийИсточник.Сумма КАК Сумма
    |ПОМЕСТИТЬ ВТ_ДанныеАкта
    |ИЗ
    |   &ВнешнийИсточникТЧ КАК ВнешнийИсточник;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   ВТ.Номенклатура,
    |   СУММА(ВТ.Сумма) КАК ОбщаяСумма
    |ИЗ
    |   ВТ_ДанныеАкта КАК ВТ
    |СГРУППИРОВАТЬ ПО
    |   ВТ.Номенклатура";

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

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

Способ 3. Реализация в СКД через Набор данных - Объект

Если задача решается в рамках Системы Компоновки Данных (СКД), использование временных таблиц для незаписанных данных может быть излишне сложным. В СКД для этих целей идеально подходит механизм под названием «Набор данных - объект». Он позволяет передать произвольную таблицу значений напрямую в компоновщик макета.

Проанализируем алгоритм реализации этого метода:

  1. Откройте схему компоновки данных (СКД) вашего отчета.
  2. Добавьте новый набор данных и выберите тип «Набор данных - объект» вместо стандартного «Набор данных - запрос».
  3. В поле «Имя объекта содержащего данные» впишите уникальный идентификатор, например, ТаблицаАкта.
  4. В списке полей набора данных вручную добавьте колонки, которые будут приходить из таблицы значений (например, Номенклатура с типом СправочникСсылка.Номенклатура и Сумма с типом Число).
  5. В модуле отчета (в предопределенной процедуре ПриКомпоновкеРезультата) выполните программную инициализацию процессора компоновки и передайте выгруженную таблицу значений в структуру внешних наборов данных.

Посмотрим на пример программного кода в модуле отчета:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // Получаем таблицу значений из реквизитов формы или объекта
    ТаблицаДляСКД = ЭтотОбъект.ПолучитьТабличнуюЧастьАкта(); 
    
    // Создаем структуру для передачи внешних наборов данных
    ВнешниеНаборыДанных = Новый Структура;
    ВнешниеНаборыДанных.Вставить("ТаблицаАкта", ТаблицаДляСКД);
    
    // Выполняем стандартную инициализацию компоновки
    СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
КонецПроцедуры

Использование набора данных-объекта избавляет вас от необходимости писать сложные SQL-запросы для ручного развертывания табличных частей и делает разработку отчета по временным или незаписанным данным простой и прозрачной.

Современные возможности платформы: Оператор ДОБАВИТЬ (начиная с версии 8.3.25)

Если вы работаете на современной версии платформы «1С:Предприятие» (8.3.25 и выше), вам доступен принципиально новый и невероятно удобный механизм — ключевое слово ДОБАВИТЬ (аналог SQL-конструкции ADD во временных таблицах).

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

Теперь мы можем наполнять созданную временную таблицу постепенно, шаг за шагом добавляя в нее данные из разных источников. Рассмотрим, как это выглядит на практике:


// Шаг 1. Создаем временную таблицу на основе первой ТЗ
ВЫБРАТЬ
    ТЧ1.Номенклатура КАК Номенклатура,
    ТЧ1.Количество КАК Количество
ПОМЕСТИТЬ ВТ_СводныеДанные
ИЗ
    &ТЗ_ПервыйДокумент КАК ТЧ1;
    
// Шаг 2. Добавляем данные из второй ТЗ в эту же временную таблицу
ВЫБРАТЬ
    ТЧ2.Номенклатура КАК Номенклатура,
    ТЧ2.Количество КАК Количество
ДОБАВИТЬ ВТ_СводныеДанные
ИЗ
    &ТЗ_ВторойДокумент КАК ТЧ2;

Обратите внимание, что при использовании оператора ДОБАВИТЬ структура полей (названия и типы колонок) в последующих запросах должна строго соответствовать структуре исходной временной таблицы, заданной при её создании (через ПОМЕСТИТЬ). Этот подход существенно упрощает разработку динамических отчетов, модулей расчета себестоимости и других алгоритмов со сложной логикой сбора данных.

Резюме

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

  1. Если документ записан в базу данных, используйте прямое обращение к физической табличной части документа (Способ 1). Это самый быстрый и производительный метод.
  2. Если данные находятся в памяти компьютера и документ еще не записан, выгружайте ТЧ в таблицу значений и передавайте ее в качестве параметра запроса (Способ 2).
  3. Для разработки гибких отчетов в СКД по произвольным или незаписанным данным применяйте Набор данных - Объект (Способ 3).
  4. Если вы работаете на современных версиях платформы 1С (8.3.25+), используйте конструкцию ДОБАВИТЬ для поэтапного наполнения плоских временных таблиц из разных источников (Способ 4).
← На главную