Что лежит во временной таблице: три способа заглянуть внутрь
Содержание
Прочитать временную таблицу в 1С можно тремя способами: следующим запросом пакета ВЫБРАТЬ * ИЗ ВТ_Имя, через коллекцию МенеджерВременныхТаблиц.Таблицы и метод ПолучитьДанные() или методом ВыполнитьПакетСПромежуточнымиДанными(). Первый работает в любой версии 8.x, два других — в платформе 8.3 (появились в релизах 8.3.4–8.3.5) и удобнее всего для отладки.
Какой выбрать? Если данные нужны дальше в алгоритме — читайте запросом. Если нужно понять, почему отчёт или проведение считают не то, — смотрите содержимое через менеджер прямо в отладчике или включайте промежуточные данные пакета. Консоль запросов полезна, когда кода под рукой нет, а запрос есть.
Как обратиться к временной таблице в тексте запроса
Временную таблицу создаёт конструкция ПОМЕСТИТЬ. Читают её так же, как обычную таблицу базы, — по имени в разделе ИЗ. Главное условие: создание и чтение должны быть разными запросами пакета, разделёнными точкой с запятой.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| Номенклатура.Артикул КАК Артикул
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Товары.Номенклатура,
| ВТ_Товары.Артикул
|ИЗ
| ВТ_Товары КАК ВТ_Товары";
Результат = Запрос.Выполнить();
Что здесь важно:
- 📌 Имя таблицы пишется без префиксов вроде
Справочник.— простоВТ_Товары. ПрефиксВТ_— соглашение, а не требование платформы, но с ним текст читается проще. - 📌 Метод
Выполнить()возвращает результат последнего запроса пакета. Поэтому читающий запрос ставят в конец. - 📌 Псевдоним
КАК ВТ_Товарынеобязателен, но конструктор запросов его добавляет, и с ним удобнее делать соединения. - 📌 К временной таблице можно присоединять таблицы базы:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(...), фильтроватьГДЕ Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТ_Товары).
Временную таблицу можно читать в виртуальных таблицах как параметр отбора: РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (ВЫБРАТЬ ВТ.Номенклатура ИЗ ВТ_Товары КАК ВТ)). Отбор внутри параметров виртуальной таблицы обычно быстрее, чем фильтр в ГДЕ после неё.
Сколько живёт временная таблица и где она хранится
Чтобы прочитать таблицу, она должна существовать в момент чтения. Тут большинство вопросов и возникает.
| Ситуация | Сколько живёт ВТ |
|---|---|
У запроса не задан МенеджерВременныхТаблиц | Только пока выполняется пакет. После Выполнить() её уже нет |
Задан Запрос.МенеджерВременныхТаблиц | Пока жив объект менеджера или до вызова Менеджер.Закрыть() |
В пакете есть УНИЧТОЖИТЬ ВТ_Имя | До этого запроса пакета |
Если таблица нужна в нескольких запросах, которые выполняются в разных местах кода, создайте менеджер явно и передавайте его всем запросам:
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос1 = Новый Запрос;
Запрос1.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос1.Текст = "ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Остатки ИЗ ...";
Запрос1.Выполнить();
// Где-то дальше в той же процедуре или в вызванной функции
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос2.Текст = "ВЫБРАТЬ * ИЗ ВТ_Остатки";
Выборка = Запрос2.Выполнить().Выбрать();
Где физически лежат данные:
- 🗄️ Клиент-серверный вариант — во временных таблицах СУБД (в MS SQL это база
tempdb, в PostgreSQL — временные таблицы сеанса). - 💾 Файловый вариант — во временных файлах и памяти процесса, который выполняет запрос.
Из этого следуют ограничения. МенеджерВременныхТаблиц доступен только на сервере: на клиент его не передать и в реквизит формы не положить. Между двумя серверными вызовами с формы он не сохраняется — каждый вызов начинает с чистого листа.
Менеджер с крупными временными таблицами держит ресурсы СУБД, пока жив. В длинных обработках закрывайте его явно: МенеджерВТ.Закрыть(), а ненужные промежуточные таблицы удаляйте запросом УНИЧТОЖИТЬ.
Программное чтение: выборка, выгрузка и коллекция Таблицы
Обычная выборка строк
Результат читающего запроса обходят выборкой или выгружают в таблицу значений:
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(СтрШаблон("%1 — %2", Выборка.Номенклатура, Выборка.Артикул));
КонецЦикла;
// Или целиком
ТЗ = Результат.Выгрузить();
Выборка расходует меньше памяти: строки читаются порциями. Выгрузить() удобна, когда нужен поиск, сортировка или передача на клиент.
Перед обходом полезна проверка Если Результат.Пустой() Тогда. Она дешевле, чем выгружать пустую таблицу и считать строки.
Чтение ВТ без отдельного запроса
С платформы 8.3.5 у менеджера есть свойство Таблицы. Это коллекция временных таблиц, которые в нём сейчас лежат. У каждого элемента есть Имя, Колонки и метод ПолучитьДанные(), который возвращает РезультатЗапроса.
ТаблицаВТ = МенеджерВТ.Таблицы.Найти("ВТ_Остатки");
Если ТаблицаВТ <> Неопределено Тогда
ТЗ = ТаблицаВТ.ПолучитьДанные().Выгрузить();
КонецЕсли;
// Список всех временных таблиц менеджера
Для Каждого Т Из МенеджерВТ.Таблицы Цикл
Сообщить(Т.Имя);
КонецЦикла;
Так удобно проверять, создалась ли таблица, и смотреть её состав колонок без правки текста запроса.
Промежуточные данные пакета
Запрос.ВыполнитьПакетСПромежуточнымиДанными() возвращает массив результатов по каждому запросу пакета. Для запросов с ПОМЕСТИТЬ в массиве оказывается содержимое временной таблицы на момент её создания.
Результаты = Запрос.ВыполнитьПакетСПромежуточнымиДанными();
ДанныеВТ = Результаты[0].Выгрузить(); // первый запрос пакета — ВТ_Товары
Обычный ВыполнитьПакет() тоже возвращает массив, но для запросов с ПОМЕСТИТЬ там лежит одна строка с колонкой Количество — сколько записей попало в таблицу. Это быстрый способ проверить, что ВТ не пустая, без чтения данных.
Метод с промежуточными данными предназначен для отладки: он тянет все данные всех временных таблиц, в рабочем коде его не оставляйте.
Пример: на каком шаге пакета пропали строки
Типичная задача отладки — отчёт выводит меньше позиций, чем ожидалось, а пакет состоит из пяти-шести запросов. Вместо того чтобы читать каждую ВТ вручную, выведите число строк по всем запросам сразу:
Результаты = Запрос.ВыполнитьПакет();
Для Индекс = 0 По Результаты.ВГраница() Цикл
Р = Результаты[Индекс];
Если Р = Неопределено Тогда
Продолжить; // страховка на случай запроса без результата
КонецЕсли;
ТЗ = Р.Выгрузить();
Если ТЗ.Колонки.Найти("Количество") <> Неопределено И ТЗ.Количество() = 1 Тогда
Сообщить(СтрШаблон("Запрос %1: в ВТ помещено %2", Индекс + 1, Формат(ТЗ[0].Количество, "ЧН=0; ЧГ=0")));
Иначе
Сообщить(СтрШаблон("Запрос %1: строк в результате %2", Индекс + 1, Формат(ТЗ.Количество(), "ЧН=0; ЧГ=0")));
КонецЕсли;
КонецЦикла;
Если, например, во второй ВТ 1 200 строк, а в третьей уже 40, — ищите проблему в условии или соединении третьего запроса. Чаще всего это внутреннее соединение там, где нужно левое, или отбор по пустому параметру.
Учтите: колонка Количество может быть и в обычном запросе, если вы так назвали поле. Пример выше — инструмент для быстрой оценки, а не точный разбор.
Как посмотреть временную таблицу в консоли запросов и отладчике
Консоль запросов
Консоль — внешняя обработка. Её открывают в режиме 1С:Предприятие через главное меню: ≡ → Файл → Открыть. Нужно право на интерактивное открытие внешних обработок; у обычного пользователя его часто нет.
Дальше порядок такой:
- Вставьте текст запроса целиком, со всеми запросами пакета.
- Если нужно увидеть конкретную ВТ, а не итог, добавьте в конец пакета
ВЫБРАТЬ * ИЗ ВТ_Имя. - Задайте параметры (
&Период,&Организация) — без них запрос упадёт или вернёт пустоту. - Выполните запрос и смотрите результат последнего пакета.
Многие сборки консоли умеют показывать результат каждого запроса пакета отдельно — они как раз используют промежуточные данные. Если в вашей консоли такого режима нет, приём с ВЫБРАТЬ * в конце работает всегда.
Отладчик
Самый быстрый способ, когда запрос сидит в типовом коде:
- Поставьте точку останова в Конфигураторе или EDT сразу после
Запрос.Выполнить(). - Когда выполнение остановится, откройте окно вычисления выражения (
Shift+F9). - Введите выражение:
Запрос.МенеджерВременныхТаблиц.Таблицы.Найти("ВТ_Остатки").ПолучитьДанные().Выгрузить()
Отладчик покажет таблицу значений, её можно развернуть и просмотреть строки.
Если у запроса менеджер не задан, после Выполнить() таблиц уже нет. Тогда вычислите в отладчике Запрос.ВыполнитьПакетСПромежуточнымиДанными() прямо на точке останова перед выполнением — получите массив результатов по всем запросам пакета.
Почему временная таблица не читается: разбор ошибок
Ещё две частые ловушки, которые не дают ошибки, но портят результат:
- ⚠️ ВТ создаётся и читается в одном запросе. Подзапрос
ИЗ ВТ_Товарывнутри того же запроса, где стоитПОМЕСТИТЬ ВТ_Товары, работать не будет. Чтение — только в следующем запросе пакета. - ⚠️ Повторное соединение с одной ВТ без индекса. Если таблица большая и по ней идёт соединение, добавьте
ИНДЕКСИРОВАТЬ ПОполям соединения. На маленьких таблицах (сотни строк) индекс не даёт выигрыша и только тратит время на построение.
Временная таблица или таблица значений: что быстрее
Частый вопрос: зачем возиться с ВТ, если можно выгрузить данные в таблицу значений и работать с ней в коде. Ответ зависит от того, что дальше делать с данными.
| Критерий | Временная таблица | Таблица значений |
|---|---|---|
| Где хранится | В СУБД (клиент-сервер) или во временных файлах | В памяти процесса 1С |
| Соединение с таблицами базы | Да, внутри запроса | Только после помещения в запрос параметром |
| Индексы | ИНДЕКСИРОВАТЬ ПО | Индексы.Добавить() для поиска в коде |
| Передача на клиент | Нет | Да (через реквизит формы или как значение) |
| Большой объём (сотни тысяч строк) | Нагрузка на СУБД, память процесса не растёт | Растёт память рабочего процесса |
| Обход в цикле и правка строк | Нет, только чтение запросом | Да |
Практическое правило:
- 🔹 Данные дальше идут в соединение с регистрами и справочниками — оставляйте их во временной таблице. Выгрузка в ТЗ и обратная загрузка параметром удвоит работу.
- 🔹 Нужно пройти по строкам, что-то посчитать, поменять значения или показать пользователю — выгружайте в таблицу значений.
- 🔹 Таблицу значений из кода нужно использовать в запросе — поместите её во временную таблицу первым запросом пакета:
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Строки
|ИЗ
| &ТаблицаСтрок КАК Т";
Запрос.УстановитьПараметр("ТаблицаСтрок", ТЗ);
Колонки ТЗ должны быть типизированы: ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число")).
Передавать в запрос таблицу значений и сразу соединять её с регистром в одном запросе — плохая идея на больших объёмах. Сначала ПОМЕСТИТЬ, при необходимости с индексом, потом соединение отдельным запросом.
Частые вопросы
Можно ли прочитать временную таблицу из другого сеанса или пользователя?
Можно ли сохранить МенеджерВременныхТаблиц в реквизите формы?
Как узнать, какие колонки есть у временной таблицы?
МенеджерВТ.Таблицы.Найти("ВТ_Имя").Колонки: у каждой колонки есть имя и тип значения. Работает в платформе 8.3.5 и новее.Влияет ли регистр букв в имени ВТ?
ВТ_Товары и вт_товары — одна таблица.Нужно ли уничтожать временные таблицы в конце процедуры?
УНИЧТОЖИТЬ или Закрыть() нужно в длинных обработках и циклах, где таблицы накапливаются.