Временная таблица в запросе 1С: ПОМЕСТИТЬ, пакетный запрос и менеджер временных таблиц
Временные таблицы позволяют разбить сложный запрос на понятные шаги: сначала отобрать нужные ссылки, а потом получить по ним остатки, цены и обороты. В статье — как работает ПОМЕСТИТЬ в запросе 1С, чем пакетный запрос отличается от нескольких отдельных, когда нужен менеджер временных таблиц, что и когда индексировать, что добавила версия 8.3.25 и почему временная таблица обычно лучше вложенного запроса.
Зачем нужна временная таблица в запросе 1С
Временная таблица — это набор данных, который запрос сохраняет на время работы, чтобы следующие запросы могли обращаться к нему как к обычной таблице. Она нужна, когда задачу неудобно или дорого решать одним «плоским» запросом: сначала отобрать небольшой набор ссылок, затем по нему получить остатки, цены и обороты. Платформа создаёт такие таблицы на стороне СУБД (в файловом варианте — средствами файловой базы) и сама удаляет их, когда они больше не нужны.
Основы запросов — параметры, условия, выборка — описаны в статье про запросы в 1С. Здесь разберём всё, что связано с временными таблицами: ПОМЕСТИТЬ, пакетный запрос, менеджер временных таблиц, индексы и отличие от вложенного запроса.
ПОМЕСТИТЬ в запросе 1С: создание временной таблицы
Временную таблицу создаёт предложение ПОМЕСТИТЬ, которое пишется после списка полей. Структура таблицы (колонки и их типы) берётся из самого запроса, поэтому псевдонимы полей становятся именами колонок.
ВЫБРАТЬ
Товары.Номенклатура КАК Номенклатура,
СУММА(Товары.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_ТоварыДокумента
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Ссылка = &Документ
СГРУППИРОВАТЬ ПО
Товары.Номенклатура
Имя временной таблицы произвольное, но удобно начинать его с префикса ВТ: так в длинном тексте сразу видно, где обращение к временной таблице, а где к объекту метаданных. Имена документов и регистров в примерах условные — подставьте объекты своей конфигурации.
Пакетный запрос: несколько запросов в одном тексте
Временная таблица живёт, пока жив её менеджер. Если менеджер явно не задан, таблица доступна только внутри одного пакета — нескольких запросов в одном тексте, разделённых точкой с запятой. Классический пример: собрать товары документа, а затем получить остатки только по ним.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Товары.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТ_Номенклатура
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка = &Документ
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(
| &НаДату,
| Номенклатура В
| (ВЫБРАТЬ
| ВТ.Номенклатура
| ИЗ
| ВТ_Номенклатура КАК ВТ)) КАК Остатки";
Запрос.УстановитьПараметр("Документ", Документ);
Запрос.УстановитьПараметр("НаДату", ТекущаяДатаСеанса());
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// обработка остатков
КонецЦикла;
Обратите внимание: условие на номенклатуру передано в параметры виртуальной таблицы остатков, а не в ГДЕ. Так СУБД считает остатки только по нужным товарам, а не по всему регистру. Временная таблица здесь делает текст простым и избавляет от соединения виртуальной таблицы с табличной частью.
Выполнить или ВыполнитьПакет
| Метод | Что возвращает | Когда использовать |
|---|---|---|
Выполнить() | Результат последнего запроса пакета | Нужен только итоговый набор данных |
ВыполнитьПакет() | Массив результатов всех запросов пакета | Из одного пакета нужно получить несколько наборов, например шапку и строки |
ВыполнитьПакетСПромежуточнымиДанными() | Массив результатов, где для запросов с ПОМЕСТИТЬ возвращается содержимое временной таблицы | Отладка: посмотреть, что попало в каждую временную таблицу |
В массиве, который возвращает ВыполнитьПакет(), элемент для запроса с ПОМЕСТИТЬ содержит не данные таблицы, а одну строку с полем Количество — числом помещённых записей. Иногда это удобно для проверки, что таблица не пустая, без отдельного запроса. Методы ВыполнитьПакетСПромежуточнымиДанными() и свойство МенеджерВременныхТаблиц.Таблицы появились в версии 8.3.8.
Менеджер временных таблиц
Когда временная таблица нужна нескольким разным запросам — например, в одном месте её заполняют, а в нескольких процедурах используют, — создайте объект МенеджерВременныхТаблиц и присвойте его каждому запросу. Пока менеджер существует, его таблицы доступны всем запросам, которые с ним связаны.
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Заказ
|ИЗ
| &ТаблицаЗаказа КАК Т";
Запрос.УстановитьПараметр("ТаблицаЗаказа", ТаблицаЗаказа); // таблица значений
Запрос.Выполнить();
// В другой процедуре тот же менеджер:
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос2.Текст =
"ВЫБРАТЬ
| ВТ_Заказ.Номенклатура КАК Номенклатура,
| ВТ_Заказ.Количество КАК Количество,
| ЕСТЬNULL(Цены.Цена, 0) КАК Цена
|ИЗ
| ВТ_Заказ КАК ВТ_Заказ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&НаДату, ) КАК Цены
| ПО ВТ_Заказ.Номенклатура = Цены.Номенклатура";
Запрос2.УстановитьПараметр("НаДату", ТекущаяДатаСеанса());
Результат = Запрос2.Выполнить().Выгрузить();
МенеджерВТ.Закрыть(); // освобождаем таблицы, когда они больше не нужны
Первый запрос показывает ещё один важный приём: таблицу значений нельзя соединить с таблицами базы напрямую, но её можно выбрать параметром ИЗ &ТаблицаЗаказа и поместить во временную таблицу. Колонки переданной таблицы значений должны быть типизированы, иначе запрос выдаст ошибку «Тип не может быть выбран в запросе». Подробнее о типах колонок — в статье про таблицу значений.
Менеджер освобождает таблицы при вызове Закрыть() или когда на него не остаётся ссылок. Не храните менеджер в долгоживущих переменных без надобности: временные таблицы занимают ресурсы СУБД всё время, пока менеджер жив.
Индексирование временных таблиц
Предложение ИНДЕКСИРОВАТЬ ПО создаёт индекс по перечисленным полям. Он ускоряет соединения и условия по этим полям, но сам требует времени на построение. Стандарт 1С «Использование временных таблиц» советует не индексировать маленькие таблицы — менее 1000 записей. Практическое правило такое:
- индексируйте поля, по которым временная таблица соединяется с другими таблицами или по которым на неё накладывается условие;
- порядок полей в индексе должен соответствовать условию соединения: индекс по
(Номенклатура, Склад)полезен для условия по обоим полям или только по первому; - не индексируйте таблицу «на всякий случай», если она используется один раз и целиком;
- не индексируйте маленькие таблицы — выигрыша не будет.
В версии 8.3.25 возможности индексирования расширены: для одной временной таблицы можно создать несколько индексов, а также уникальный индекс. Синтаксис, описанный в анонсе на сайте 1С, выглядит так:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
Номенклатура.Код КАК Код,
Номенклатура.Артикул КАК Артикул
ПОМЕСТИТЬ ВТ_Товары
ИЗ
Справочник.Номенклатура КАК Номенклатура
ИНДЕКСИРОВАТЬ ПО НАБОРАМ
((Ссылка) УНИКАЛЬНО, (Артикул))
Индексы создаются только при первом помещении данных в таблицу. Если ваша конфигурация работает в режиме совместимости с более ранней версией платформы, новые конструкции могут быть недоступны — проверьте это до того, как использовать их в типовом коде.
ДОБАВИТЬ: дописать данные в существующую таблицу (8.3.25)
Раньше повторный ПОМЕСТИТЬ в таблицу с тем же именем заканчивался ошибкой «Временная таблица уже существует», и данные из нескольких источников приходилось объединять через ОБЪЕДИНИТЬ ВСЕ в одном запросе. С версии 8.3.25 появилось предложение ДОБАВИТЬ:
ВЫБРАТЬ
Номенклатура.Код КАК Код,
Номенклатура.Наименование КАК Наименование
ПОМЕСТИТЬ ВТ_Позиции
ИЗ
Справочник.Номенклатура КАК Номенклатура
;
ВЫБРАТЬ
Услуги.Код КАК Код,
Услуги.Наименование КАК Наименование
ДОБАВИТЬ ВТ_Позиции
ИЗ
Справочник.Услуги КАК Услуги
Состав колонок и их типы в добавляющем запросе должны совпадать со структурой таблицы, иначе возникнет ошибка о несоответствии структуры.
УНИЧТОЖИТЬ: удаление временной таблицы
Если пакет длинный, а промежуточная таблица большая, её можно удалить, как только она перестала быть нужна:
УНИЧТОЖИТЬ ВТ_Номенклатура
Это освобождает ресурсы до конца выполнения пакета. В конце пакета без менеджера временных таблиц платформа удалит таблицы сама, поэтому УНИЧТОЖИТЬ полезен прежде всего в длинных пакетах и при работе с менеджером, который продолжает жить.
Вложенный запрос 1С или временная таблица
Вложенный запрос — это запрос внутри другого запроса: в списке источников (ИЗ (ВЫБРАТЬ ...) КАК Т) или в условии (В (ВЫБРАТЬ ...)). Синтаксически он часто заменяет временную таблицу, но ведёт себя иначе.
| Вложенный запрос | Временная таблица | |
|---|---|---|
| Где хранится результат | Нигде, СУБД сама решает, как выполнить | Материализуется в отдельной таблице |
| Индексы | Нет | Можно задать ИНДЕКСИРОВАТЬ ПО |
| Повторное использование | Нужно повторять текст | Таблица доступна всем запросам пакета или менеджера |
| Предсказуемость плана | Ниже, особенно в соединениях | Выше: СУБД знает объём данных |
Стандарт «Ограничения на соединения с вложенными запросами и виртуальными таблицами» прямо рекомендует не соединять таблицы с вложенными запросами, а соединять друг с другом только объекты метаданных или временные таблицы. Вложенный запрос в условии В (ВЫБРАТЬ ...), как в примере с остатками выше, допустим и широко используется. Когда сомневаетесь — выносите подзапрос во временную таблицу: текст станет понятнее, а план выполнения стабильнее. О видах соединений подробно рассказано в статье про соединения в запросах.
Отладка запросов с временными таблицами
Самая частая задача при отладке — увидеть, что лежит во временной таблице. Начиная с 8.3.8 это можно сделать прямо в отладчике, вычислив выражение:
// Если у запроса есть менеджер временных таблиц:
Запрос.МенеджерВременныхТаблиц.Таблицы[0].ПолучитьДанные().Выгрузить()
// Если менеджера нет — выполнить пакет с промежуточными данными:
Запрос.ВыполнитьПакетСПромежуточнымиДанными()[0].Выгрузить()
Индекс в квадратных скобках — номер таблицы в менеджере или номер запроса в пакете, начиная с нуля. Учтите, что второй способ повторно выполняет весь пакет. Подробнее о самом отладчике — в статье про отладку в 1С.
Типичные ошибки
- «Таблица не найдена» — запрос обращается к временной таблице, созданной в другом запросе без общего менеджера, или менеджер уже закрыт.
- «Временная таблица уже существует» — повторный
ПОМЕСТИТЬс тем же именем в рамках одного менеджера. Удалите таблицу черезУНИЧТОЖИТЬ, дайте другое имя или, начиная с 8.3.25, используйтеДОБАВИТЬ. - «Тип не может быть выбран в запросе» — в запрос передана таблица значений с нетипизированными колонками.
- Условие в ГДЕ вместо параметров виртуальной таблицы — остатки и обороты считаются по всему регистру, а потом фильтруются.
- Индексы на маленьких таблицах — лишняя работа без выигрыша.
- Соединение с вложенным запросом вместо временной таблицы — нестабильный и медленный план на больших объёмах.
Актуальность для новых версий
Конструкции ПОМЕСТИТЬ, ИНДЕКСИРОВАТЬ ПО и УНИЧТОЖИТЬ работают во всех версиях 8.3. Метод ВыполнитьПакетСПромежуточнымиДанными() и свойство МенеджерВременныхТаблиц.Таблицы доступны с версии 8.3.8. В версии 8.3.25 появились предложение ДОБАВИТЬ, несколько индексов на одной временной таблице и уникальные индексы; при работе в режиме совместимости с более ранней версией их доступность нужно проверять.
Частые вопросы
Чем временная таблица отличается от вложенного запроса?
Результат временной таблицы сохраняется отдельно, его можно проиндексировать и использовать в нескольких запросах пакета. Вложенный запрос не материализуется, и в соединениях СУБД может выбрать для него неудачный план. Стандарты 1С не рекомендуют соединять таблицы с вложенными запросами.
Сколько живёт временная таблица?
Без менеджера временных таблиц — до окончания выполнения пакета запросов. С менеджером — пока менеджер существует или пока не вызван его метод Закрыть, либо пока таблица не удалена через УНИЧТОЖИТЬ.
Нужно ли всегда индексировать временную таблицу?
Нет. Индексируют поля, по которым таблица соединяется с другими или по которым на неё накладывается условие. Стандарт 1С советует не индексировать маленькие таблицы, менее 1000 записей.
Как посмотреть содержимое временной таблицы при отладке?
Начиная с 8.3.8 в отладчике можно вычислить Запрос.МенеджерВременныхТаблиц.Таблицы[0].ПолучитьДанные().Выгрузить() или, если менеджера нет, Запрос.ВыполнитьПакетСПромежуточнымиДанными()[0].Выгрузить().
Как передать таблицу значений в запрос и соединить её с регистром?
Передайте таблицу параметром, выберите из неё данные конструкцией ИЗ &Таблица КАК Т и поместите во временную таблицу. Затем соединяйте временную таблицу с регистром. Колонки таблицы значений должны иметь типы.
Можно ли добавить строки в уже созданную временную таблицу?
Начиная с версии 8.3.25 — да, предложением ДОБАВИТЬ вместо ПОМЕСТИТЬ. Структура добавляемых данных должна совпадать со структурой таблицы. В более ранних версиях данные объединяют через ОБЪЕДИНИТЬ ВСЕ до помещения в таблицу.