Решение задач 1С:Предприятие 8 на примерах

Временная таблица в запросе 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 — да, предложением ДОБАВИТЬ вместо ПОМЕСТИТЬ. Структура добавляемых данных должна совпадать со структурой таблицы. В более ранних версиях данные объединяют через ОБЪЕДИНИТЬ ВСЕ до помещения в таблицу.

Соединения в запросах 1С: левое, внутреннее, полное и ОБЪЕДИНИТЬ ВСЕ

Запросы

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

Значение в запросе 1С: ЗНАЧЕНИЕ, ВЫБОР, ЕСТЬNULL и группировка

Запросы

Как сравнивать с перечислениями и предопределёнными элементами через ЗНАЧЕНИЕ(), обрабатывать NULL после левого соединения и считать количество и суммы с группировкой и условием ИМЕЮЩИЕ.

Строка в запросе 1С: ПОДОБНО, ПОДСТРОКА и строки неограниченной длины

Запросы

Как работать со строками в языке запросов 1С: искать по шаблону через ПОДОБНО, вырезать часть строки, сравнивать поля неограниченной длины и склеивать текст без ошибок с NULL.

Ссылка в запросе 1С: разыменование, ВЫРАЗИТЬ, пустая ссылка и GUID

Запросы

Практический разбор ссылок в языке запросов 1С: разыменование и его цена, ВЫРАЗИТЬ и ТИПЗНАЧЕНИЯ для составных типов, пустая ссылка и NULL, представление и уникальный идентификатор.