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