Почему возникает ошибка «Временная таблица уже существует» и как её исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке алгоритмов с использованием языка запросов 1С программисты часто сталкиваются с исключением вида: «Временная таблица уже существует "ИмяТаблицы"». Ошибка указывает на точку выполнения запроса с конструкцией ПОМЕСТИТЬ. Проанализируем, почему возникает данная коллизия, разберем типичные ошибки в программном коде и рассмотрим правильные практики работы с объектом МенеджерВременныхТаблиц.

Причина 1: Случайный дублированный вызов метода Выполнить()

Рассмотрим типовой фрагмент кода, в котором возникает эта ошибка:


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = "ВЫБРАТЬ
|   ПроверкаРабочегоМеста.Регистратор КАК ПакетРасписаний,
|   ПроверкаРабочегоМеста.Кабинет,
|   ПроверкаРабочегоМеста.Кресло,
|   ПроверкаРабочегоМеста.ВремяНачала,
|   ПроверкаРабочегоМеста.ВремяОкончания
|ПОМЕСТИТЬ ВТ_Расписание
|ИЗ
|   РегистрСведений.ПроверкаРабочегоМеста КАК ПроверкаРабочегоМеста";

Запрос.Выполнить();

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Выясним причину сбоя: метод Запрос.Выполнить() написан в коде дважды подряд. При первом вызове платформа 1С успешно выполняет запрос к СУБД, создает в менеджере временную таблицу ВТ_Расписание и сохраняет её структуру и данные. При повторном вызове Запрос.Выполнить() платформа пытается заново выполнить этот же текст, обращаясь к тому же объекту МенеджерВременныхТаблиц, в котором таблица с таким именем уже зарегистрирована. СУБД возвращает ошибку дублирования имени.

Кроме того, обратим внимание на логическую ошибку: запрос, содержащий только оператор ПОМЕСТИТЬ, не возвращает итоговой выборки строк. Вызов РезультатЗапроса.Выбрать() для такого запроса не имеет смысла, так как результат будет пустым.

Чтобы получить данные и обойтись без создания лишней временной таблицы, если она не нужна для последующих расчетов, достаточно убрать директиву ПОМЕСТИТЬ и убрать лишний вызов:


Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|   ПроверкаРабочегоМеста.Регистратор КАК ПакетРасписаний,
|   ПроверкаРабочегоМеста.Кабинет,
|   ПроверкаРабочегоМеста.Кресло,
|   ПроверкаРабочегоМеста.ВремяНачала,
|   ПроверкаРабочегоМеста.ВремяОкончания
|ИЗ
|   РегистрСведений.ПроверкаРабочегоМеста КАК ПроверкаРабочегоМеста";

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
    // Обработка данных
КонецЦикла;

Причина 2: Повторное использование Менеджера временных таблиц в цикле или процедурах

Разберем другую распространенную ситуацию. Если переменная МенеджерВременныхТаблиц инициализирована до входа в цикл или объявлена на уровне модуля/формы, при каждой новой итерации цикла будет вызываться создание одной и той же таблицы:


МенеджерВТ = Новый МенеджерВременныхТаблиц;

Для Каждого СтрокаТаблицы Из ТаблицаДокументов Цикл
    Запрос = Новый Запрос;
    Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
    Запрос.Текст = 
        "ВЫБРАТЬ Ссылка ПОМЕСТИТЬ ВТ_Данные ИЗ Документ.ЗаказКлиента ГДЕ ...";
    Запрос.Выполнить(); // На второй итерации упадет с ошибкой
КонецЦикла;

Чтобы избежать ошибки при многократном выполнении, применим один из следующих подходов:

  1. Создание менеджера локально внутри цикла. Если данные не должны передаваться между итерациями, перенесем конструктор Новый МенеджерВременныхТаблиц внутрь цикла. При завершении итерации старый объект будет уничтожен сборщиком мусора вместе со своими таблицами.
  2. Явная очистка менеджера с помощью метода Закрыть(). Вызов МенеджерВТ.Закрыть() удаляет все физические временные таблицы на стороне СУБД и очищает коллекцию менеджера. После этого менеджер можно использовать повторно.
  3. Уничтожение временной таблицы прямо в тексте запроса. В конце пакета запросов вызовем оператор УНИЧТОЖИТЬ.

Посмотрим на пример правильного освобождения ресурсов с помощью инструкции УНИЧТОЖИТЬ:


Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Проверка.Кабинет,
    |   Проверка.Кресло
    |ПОМЕСТИТЬ ВТ_Расписание
    |ИЗ
    |   РегистрСведений.ПроверкаРабочегоМеста КАК Проверка;
    |
    |ВЫБРАТЬ
    |   ВТ_Расписание.Кабинет,
    |   ВТ_Расписание.Кресло
    |ИЗ
    |   ВТ_Расписание КАК ВТ_Расписание;
    |
    |УНИЧТОЖИТЬ ВТ_Расписание";

МассивРезультатов = Запрос.ВыполнитьПакет();
Выборка = МассивРезультатов[1].Выбрать();

Причина 3: Построение пакетных запросов без отдельного менеджера

Проанализируем механику работы пакетных запросов. Если временная таблица нужна только внутри одного запроса для промежуточных вычислений, объединений или соединений, объявлять отдельный объект МенеджерВременныхТаблиц в коде 1С не требуется вообще.

Платформа 1С умеет самостоятельно управлять жизненным циклом временных таблиц в рамках одного пакетного запроса, разделенного символом точкой с запятой (;):


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ПроверкаРабочегоМеста.Регистратор КАК ПакетРасписаний,
    |   ПроверкаРабочегоМеста.Кабинет,
    |   ПроверкаРабочегоМеста.Кресло
    |ПОМЕСТИТЬ ВТ_Расписание
    |ИЗ
    |   РегистрСведений.ПроверкаРабочегоМеста КАК ПроверкаРабочегоМеста
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   ВТ_Расписание.ПакетРасписаний,
    |   ВТ_Расписание.Кабинет,
    |   ВТ_Расписание.Кресло
    |ИЗ
    |   ВТ_Расписание КАК ВТ_Расписание";

// Метод Выполнить() возвращает результат ТОЛЬКО последнего запроса пакета:
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Обход полученных данных
КонецЦикла;

В этом случае платформа автоматически создаст временную таблицу на СУБД, прочитает из нее данные во втором запросе и корректно уничтожит её сразу после выполнения метода Выполнить(). Это защитит алгоритм от случайных дублей имен и исключит утечки памяти в базе данных.

← На главную