Как получить данные из временной таблицы в пакете запросов 1С?

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

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

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

Выясним причину: почему стандартный метод ВыполнитьПакет() возвращает пустую структуру вместо данных?

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

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

  1. Этот результат содержит ровно одну колонку с именем «Количество» и ровно одну строку.
  2. В единственной ячейке таблицы содержится число — количество записей, которые были помещены во временную таблицу.
  3. Реальные строки данных (номенклатура, цены, остатки) в этом результате отсутствуют. Попытка выгрузить этот результат через метод Выгрузить() вернет пустую таблицу значений с одной числовой колонкой.
  4. Аналогично, при уничтожении временной таблицы через оператор УНИЧТОЖИТЬ в соответствующем элементе массива результатов вернется значение Неопределено.

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

Способ 1. Добавление дополнительного запроса выборки в пакет (Универсальный метод для 1С 8.2 и 8.3)

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

Разберем этот метод по шагам на практическом примере:


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

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

// РезультатыПакета[0] — это результат создания ВТ (содержит только количество строк)
// РезультатыПакета[1] — это наш явный запрос выборки, содержащий реальные данные ВТ!
ТаблицаНоменклатуры = РезультатыПакета[1].Выгрузить();

// РезультатыПакета[2] — это итоговый результат всего пакета
ТаблицаИтогов = РезультатыПакета[2].Выгрузить();

Преимущества метода:

Недостатки метода:

Способ 2. Использование единого Менеджера временных таблиц и разделение запросов (Рекомендуемый метод для 1С 8.2)

Если промежуточные данные нужны нам не всегда, а только при определенных условиях в коде, лучшим решением для платформы 1С 8.2 будет разделение одного большого пакетного запроса на несколько отдельных объектов Запрос, связанных через общий МенеджерВременныхТаблиц.

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


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

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

// 3. Теперь, когда в коде действительно потребовались данные этой ВТ:
ЗапросИзвлечения = Новый Запрос;
ЗапросИзвлечения.МенеджерВременныхТаблиц = МенеджерВТ;
ЗапросИзвлечения.Текст = "ВЫБРАТЬ Ссылка ИЗ вт_Номенклатура";

// Получаем реальные данные в таблицу значений
ТаблицаДанных = ЗапросИзвлечения.Выполнить().Выгрузить();

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

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

Преимущества метода:

Способ 3. Метод ВыполнитьПакетСПромежуточнымиДанными() (Для платформ 1С 8.3.8 и выше)

Начиная с версии платформы 1С:Предприятие 8.3.8, у объекта Запрос появился штатный метод, созданный специально для решения описываемой проблемы — ВыполнитьПакетСПромежуточнымиДанными().

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

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


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

// Используем современный метод платформы
МассивРезультатов = Запрос.ВыполнитьПакетСПромежуточнымиДанными();

// МассивРезультатов[0] содержит полноценную таблицу с данными вт_Номенклатура!
ТаблицаНоменклатуры = МассивРезультатов[0].Выгрузить();

// МассивРезультатов[1] содержит итоговый результат
ТаблицаИтогов = МассивРезультатов[1].Выгрузить();

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

Способ 4. Программный доступ к таблицам менеджера через свойство «Таблицы» (Для платформ 1С 8.3.8 и выше)

Еще одно мощное нововведение версии 8.3.8 — это появление коллекции Таблицы у объекта МенеджерВременныхТаблиц. Теперь мы можем обращаться к любой временной таблице по её имени прямо из кода, не меняя структуру пакетного запроса и не выгружая лишние таблицы.

Проанализируем пример кода:


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Номенклатура.Ссылка КАК Ссылка
    |ПОМЕСТИТЬ вт_Номенклатура
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура";

Запрос.Выполнить(); // Запрос выполнился, ВТ создана в менеджере

// Извлекаем данные конкретной ВТ по имени напрямую из менеджера!
ВременнаяТаблица = МенеджерВТ.Таблицы.Найти("вт_Номенклатура");
Если ВременнаяТаблица <> Неопределено Тогда
    // Получаем данные в виде РезультатЗапроса и выгружаем
    ТаблицаДанных = ВременнаяТаблица.ПолучитьДанные().Выгрузить();
КонецЕсли;

Этот способ объединяет в себе гибкость старого подхода с разделением запросов и избавляет от необходимости писать лишний код выборки внутри текста запроса. Он идеально подходит для точечного извлечения данных конкретных таблиц в промышленной эксплуатации.

Разница между временными таблицами СУБД и таблицами значений на сервере 1С

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

Когда мы выгружаем данные в таблицу значений на сторону 1С (используя метод Выгрузить()), происходят следующие процессы:

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

Использование механизмов платформы для удобной отладки

Каждый 1С-разработчик сталкивается с ситуацией, когда сложный запрос выдает неверный результат, и нужно понять, на каком именно шаге (в какой временной таблице) закралась ошибка. Благодаря современным возможностям платформы 8.3.8+, изменять исходный код конфигурации для отладки больше нет необходимости.

Если вы остановились на точке останова в конфигураторе сразу после выполнения запроса, откройте окно «Табло» (сочетание клавиш Shift + F9) и используйте следующие выражения для просмотра содержимого временных таблиц в реальном времени:

  1. Если вы использовали метод ВыполнитьПакет(), но хотите прямо сейчас в отладчике увидеть промежуточные данные, вычислите выражение:
    Запрос.ВыполнитьПакетСПромежуточнымиДанными()[0].Выгрузить() *(где вместо 0 укажите индекс интересующей вас временной таблицы в пакете).*
  2. Если в запросе используется МенеджерВременныхТаблиц, вы можете посмотреть данные по имени таблицы, не задумываясь об индексах:
    Запрос.МенеджерВременныхТаблиц.Таблицы.Найти("вт_Номенклатура").ПолучитьДанные().Выгрузить()

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

← На главную