Не могу обратиться к временной таблице в пакетном запросе 1С: как исправить ошибку «Поле не обнаружено»

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

Каждый разработчик на платформе «1С:Предприятие 8» рано или поздно сталкивается с ситуацией, когда сложный запрос с временными таблицами идеально отрабатывает в интерактивной консоли запросов, но при переносе в конфигуратор начинает вызывать программные ошибки вида «Поле [ИмяПоля] не обнаружено». В этой статье мы подробно разберем, почему возникает такое поведение, что происходит с временными таблицами в СУБД «под капотом» и как правильно получать данные из пакета запросов без потери производительности системы.

Суть проблемы: почему в консоли всё работает, а в коде возникает ошибка?

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

  1. Выборка данных из табличной части документа во временную таблицу ВТ_ТЧ с помощью оператора ПОМЕСТИТЬ.
  2. Левое соединение ВТ_ТЧ с виртуальной таблицей остатков регистра бухгалтерии и помещение результата во вторую временную таблицу ВТ_Остатки.
  3. Финальная выборка из ВТ_Остатки с соединением с регистром сведений для получения итоговых данных.

В конфигураторе написан следующий код для выполнения этого запроса:


РезультатЗапроса = Запрос.ВыполнитьПакет();
Выборка = РезультатЗапроса[1].Выбрать();

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

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

Таким образом, в массиве РезультатЗапроса элемент с индексом 1 (соответствующий второму запросу пакета, где создается ВТ_Остатки) содержит не структуру полей остатков, а техническую таблицу с числом строк. Попытка вызвать метод Выбрать() и обратиться к реальным полям логично приводит к ошибке среды выполнения.

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


Вариант решения №1: Использование метода ВыполнитьПакетСПромежуточнымиДанными()

Самый простой и быстрый способ заставить код работать — заменить стандартный метод выполнения на специализированный. Рассмотрим подробнее этот подход:


РезультатЗапроса = Запрос.ВыполнитьПакетСПромежуточнымиДанными();
Выборка = РезультатЗапроса[1].Выбрать();

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

Важное предупреждение о производительности! Использовать метод ВыполнитьПакетСПромежуточнымиДанными() в реальном продакшн-коде настоятельно не рекомендуется, за исключением редких специфических задач или процессов интерактивной отладки. Почему?

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


Вариант решения №2 (Рекомендуемый): Использование Менеджера временных таблиц

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

Разберем по шагам, как настроить работу с менеджером:

  1. Создаем новый экземпляр объекта МенеджерВременныхТаблиц.
  2. Связываем его с нашим объектом Запрос.
  3. Выполняем пакет запросов обычным методом ВыполнитьПакет(). При этом временные таблицы сохраняются в базе данных СУБД (в базе tempdb) и остаются доступными для последующего чтения через менеджер.
  4. Обращаемся к нужной временной таблице по её индексу или имени напрямую через коллекцию менеджера.

Посмотрим на пример программной реализации этого подхода:


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

// 2. Выполняем пакет запросов (быстро, на стороне СУБД)
РезультатЗапроса = Запрос.ВыполнитьПакет();

// 3. Получаем данные конкретной временной таблицы по индексу
// Индекс 1 соответствует созданной таблице ВТ_Остатки
ДанныеВТ_Остатки = МенеджерВТ.Таблицы[1].ПолучитьДанные().Выбрать();

Пока ДанныеВТ_Остатки.Следующий() Цикл
    // Здесь мы можем безопасно обращаться к полям нашей временной таблицы
    Сообщить(ДанныеВТ_Остатки.Деталь + " - Остаток: " + ДанныеВТ_Остатки.КоличествоОстаток);
КонецЦикла;

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


Очистка ресурсов и управление памятью

Используя МенеджерВременныхТаблиц, разработчик должен помнить об управлении временем жизни этих таблиц в СУБД. Если вы не назначили менеджер временных таблиц явным образом, СУБД автоматически уничтожит временные таблицы сразу после завершения выполнения метода ВыполнитьПакет().

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

Чтобы избежать переполнения базы данных tempdb и нерационального удержания блокировок ресурсов СУБД, хорошим тоном считается явное уничтожение таблиц, когда они больше не требуются для логики программы:


// После завершения всех манипуляций с данными обязательно освобождаем ресурсы СУБД
МенеджерВТ.Закрыть();

Вызов метода Закрыть() мгновенно посылает команду в СУБД на удаление всех временных таблиц и их индексов, освобождая ценные системные ресурсы, не дожидаясь срабатывания автоматического сборщика мусора платформы «1С:Предприятие».


Клиент-серверные ограничения временных таблиц

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

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


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

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

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

← На главную