Работа с временными таблицами — это базовая и в то же время крайне важная часть разработки на платформе 1С 8.3. Часто возникает задача: взять данные из ТаблицыЗначений, расположенной на форме, и соединить их с данными из базы (например, с остатками или ценами). Однако новички часто сталкиваются с ошибкой «Таблица не найдена ВТ». В этой статье мы подробно разберем, почему возникает эта проблема, и рассмотрим несколько способов ее решения.
Проанализируем ситуацию: когда вы создаете временную таблицу (ВТ) в коде 1С, она физически создается в базе данных (в TempDB) только в момент вызова метода Выполнить(). Если вы просто написали текст запроса с инструкцией ПОМЕСТИТЬ ВТ, но не выполнили его, таблица фактически не существует. Кроме того, временная таблица «живет» внутри контекста конкретного менеджера временных таблиц. Если вы создаете два разных объекта Запрос и не связываете их общим менеджером, второй запрос никогда не «увидит» таблицу, созданную первым.
Самый эффективный и современный способ — объединить все действия в один текст запроса. Это избавляет от необходимости вручную управлять менеджером временных таблиц и гарантирует, что все таблицы будут видны в рамках одной транзакции к базе данных.
Рассмотрим пример реализации пакетного запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВП.Ссылка КАК Номенклатура,
| ВП.Код КАК Код
|ПОМЕСТИТЬ ВТ_ДанныеФормы
|ИЗ
| &ВПереноса КАК ВП
|;
|
////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ.Номенклатура,
| ВТ.Код,
| ЦеныНоменклатурыСрезПоследних.Цена,
| ЦеныНоменклатурыСрезПоследних.ВидЦены
|ИЗ
| ВТ_ДанныеФормы КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК ЦеныНоменклатурыСрезПоследних
| ПО ВТ.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура";
Запрос.УстановитьПараметр("ВПереноса", ТаблицаСФормы.Выгрузить());
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Результат = Запрос.Выполнить().Выгрузить();
Обратите внимание на использование символа ; между частями запроса. Это указывает платформе на то, что перед нами пакет из нескольких запросов, которые выполняются последовательно один за другим.
Бывают случаи, когда логика программы требует разделения запросов. Например, если между созданием временной таблицы и получением итоговых данных нужно выполнить какой-то промежуточный код на встроенном языке 1С. В этом случае нам поможет МенеджерВременныхТаблиц.
Разберем по шагам, как это реализовать правильно:
МенеджерВременныхТаблиц.Выполнить() для первого запроса.Посмотрим на пример кода:
// 1. Создаем менеджер
МенеджерВТ = Новый МенеджерВременныхТаблиц;
// 2. Создаем первый запрос для наполнения ВТ
Запрос1 = Новый Запрос;
Запрос1.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос1.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ ИЗ &ТЗ КАК ТЗ";
Запрос1.УстановитьПараметр("ТЗ", ВыгруженнаяТЗ);
Запрос1.Выполнить(); // ТЕПЕРЬ ТАБЛИЦА ВТ СУЩЕСТВУЕТ!
// 3. Создаем второй запрос для получения данных
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ; // Подключаем тот же менеджер
Запрос2.Текст = "ВЫБРАТЬ ВТ.Ссылка, Цены.Цена ИЗ ВТ ЛЕВОЕ СОЕДИНЕНИЕ Регистр... КАК Цены ПО ВТ.Ссылка = Цены.Номенклатура";
Результат = Запрос2.Выполнить().Выгрузить();
Просто заставить запрос работать — это только половина дела. Давайте выясним, как сделать так, чтобы он работал быстро и надежно, особенно если данных в таблице значений много.
1. Типизация колонок таблицы значений
Когда вы передаете таблицу значений в запрос через параметр, 1С пытается автоматически определить типы данных. Если колонка в ТЗ не типизирована (имеет тип Произвольный), в запросе это может привести к ошибкам сравнения. Мы рекомендуем всегда явно указывать типы при создании ТЗ:
ТЗ = Новый ТаблицаЗначений;
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("СправочникСсылка.Номенклатура"));
ОписаниеТиповНоменклатура = Новый ОписаниеТипов(МассивТипов);
ТЗ.Колонки.Добавить("Ссылка", ОписаниеТиповНоменклатура);
2. Индексация временных таблиц
Если в вашей временной таблице будет больше 50–100 записей, обязательно индексируйте поля, по которым происходит соединение. Без индекса СУБД будет вынуждена делать полный перебор таблиц (Table Scan), что катастрофически замедлит работу. В запросе это делается так:
ВЫБРАТЬ Ссылка, Код ПОМЕСТИТЬ ВТ ИНДЕКСИРОВАТЬ ПО Ссылка ИЗ &Данные
3. Оптимизация соединения со Срезом Последних
В тексте обсуждения автор использовал ЛЕВОЕ СОЕДИНЕНИЕ с виртуальной таблицей СрезПоследних. Рассмотрим ситуацию: если в регистре цен миллионы записей, а в вашей таблице всего 10 товаров, 1С сначала вычислит срез последних для всего регистра, а потом соединит его с вашими 10 товарами. Это крайне неэффективно.
Проанализируем правильный подход: передавайте список товаров из временной таблицы в параметры виртуальной таблицы. Это заставит СУБД выбрать цены только по нужным позициям:
// В параметре условия виртуальной таблицы используем вложенный запрос к нашей ВТ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура В (ВЫБРАТЬ Ссылка ИЗ ВТ)) КАК Цены
Если вы используете МенеджерВременныхТаблиц, помните, что таблицы занимают место в оперативной памяти и во временной базе данных сервера. Хорошим тоном считается явное уничтожение таблиц, когда они больше не нужны, особенно внутри длинных циклов:
ЗапросУничтожения = Новый Запрос;
ЗапросУничтожения.МенеджерВременныхТаблиц = МенеджерВТ;
ЗапросУничтожения.Текст = "УНИЧТОЖИТЬ ВТ";
ЗапросУничтожения.Выполнить();
Подведем итог: для большинства задач лучше использовать пакетные запросы (один объект Запрос, несколько инструкций через точку с запятой). Если же вы разделяете запросы, обязательно используйте один и тот же МенеджерВременныхТаблиц и не забывайте вызывать Выполнить() для каждого промежуточного этапа.