Работа с временными таблицами в 1С — это стандарт де-факто для разработки сложных отчетов и обработок. Однако многие разработчики сталкиваются с неприятной особенностью: если временная таблица была создана в одном запросе и передана в другой через МенеджерВременныхТаблиц, стандартный Конструктор запроса отказывается распознавать ее структуру. При попытке открыть такой запрос мы получаем сообщение об ошибке: «Таблица не найдена» или «У данной таблицы отсутствуют выбранные поля».
В этой статье мы подробно разберем, почему это происходит, и рассмотрим несколько эффективных способов решения проблемы — от «правильного» проектирования через пакетные запросы до хитроумных лайфхаков с подменой текста запроса.
Для начала проанализируем ситуацию с точки зрения платформы. Конструктор запроса — это инструмент статического анализа текста. Когда мы открываем его, он пытается распарсить текст запроса и сопоставить имена таблиц с метаданными конфигурации или с описаниями внутри текущего текста запроса.
Если мы используем МенеджерВременныхТаблиц, описание структуры таблицы хранится в оперативной памяти сервера (или в системной базе tempdb SQL-сервера) в контексте конкретного сеанса. Конструктор, работающий внутри конфигуратора, не имеет доступа к данным, которые были сформированы в режиме «Предприятие». Он видит только «голый» текст: ВЫБРАТЬ * ИЗ ВтДоговоры. Поскольку таблицы ВтДоговоры нет в метаданных и она не создается выше по тексту в этом же пакете, конструктор выдает ошибку.
Это самый рекомендуемый и современный способ. Если логика вашей задачи позволяет объединить создание временной таблицы и ее последующее использование в один процесс, лучше всего использовать пакетный запрос. В этом случае конструктор будет видеть всю цепочку создания таблиц.
Рассмотрим пример создания пакетного запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДоговорыКонтрагентов.Ссылка КАК Договор
|ПОМЕСТИТЬ ВтДоговоры
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
|ГДЕ
| ДоговорыКонтрагентов.ВидДоговора = &ВидДоговора
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВтДоговоры.Договор,
| Взаиморасчеты.СуммаОстаток
|ИЗ
| ВтДоговоры КАК ВтДоговоры
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Взаиморасчеты.Остатки КАК Взаиморасчеты
| ПО ВтДоговоры.Договор = Взаиморасчеты.Договор";
РезультатыПакеты = Запрос.ВыполнитьПакет();
В данном примере конструктор запроса на вкладке «Пакет запросов» позволит вам легко переключаться между частями запроса, и во второй части он будет знать структуру таблицы ВтДоговоры. Для автоматической очистки создаваемых данных в длинных пакетах можно использовать обработку для оптимизации временных таблиц в пакетных запросах.
Бывают случаи (как у автора темы), когда одна временная таблица должна использоваться в разных процедурах обработки при нажатии на разные кнопки. В этом случае без МенеджерВременныхТаблиц не обойтись. Чтобы конструктор не ругался, мы можем временно добавлять в начало запроса блок создания таблицы с нужными полями. Для анализа и отладки подобных цепочек подойдёт инструмент для фиксации и просмотра временных таблиц при отладке.
Разберем по шагам, как реализовать этот метод:
Посмотрим на пример кода, реализующего этот подход:
// В конструкторе текст выглядит так:
// ВЫБРАТЬ ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка) КАК Договор ПОМЕСТИТЬ ВтДоговоры;
// //МАРКЕР_КОНЦА_ОПИСАНИЯ
// ВЫБРАТЬ Договор ИЗ ВтДоговоры ГДЕ Сумма > 100
ТекстДляКонструктора = Запрос.Текст;
ПозицияМаркера = СтрНайти(ТекстДляКонструктора, "//МАРКЕР_КОНЦА_ОПИСАНИЯ");
Если ПозицияМаркера > 0 Тогда
// Отрезаем всё лишнее, оставляя только реальный запрос
Запрос.Текст = Сред(ТекстДляКонструктора, ПозицияМаркера + СтрДлина("//МАРКЕР_КОНЦА_ОПИСАНИЯ") + 1);
КонецЕсли;
Запрос.МенеджерВременныхТаблиц = МойМенеджерВТ;
Результат = Запрос.Выполнить();
Если временная таблица не содержит миллионы записей (до 20-50 тысяч строк), хорошей альтернативой МенеджерВременныхТаблиц будет выгрузка данных в ТаблицаЗначений.
Рассмотрим, как это помогает в работе с конструктором:
ТЗ.ТЗ как параметр.Пример кода с загрузкой ТЗ в запрос:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗДоговоры.Договор
|ПОМЕСТИТЬ ВтДоговоры
|ИЗ
| &ТЗДоговоры КАК ТЗДоговоры
|;
|ВЫБРАТЬ * ИЗ ВтДоговоры";
Запрос.УстановитьПараметр("ТЗДоговоры", МояТаблицаДоговоров);
Запрос.Выполнить();
Проанализируем ситуацию, упомянутую в обсуждении: стоит ли долго держать открытым МенеджерВременныхТаблиц?
Риски использования МВТ:
Рекомендация: Если данные (например, список из 17 000 договоров) нужны вам для разных действий в форме, лучше всего один раз получить их в массив ссылок или Таблицу Значений. Передача массива ссылок в параметр ГДЕ Ссылка В (&МассивДоговоров) обычно работает быстрее и стабильнее, чем поддержание постоянного МенеджерВременныхТаблиц в памяти сервера в течение всего рабочего дня пользователя.
Мы выяснили, что «слепота» конструктора запросов — это не баг, а архитектурная особенность. Для комфортной разработки мы рекомендуем использовать пакетные запросы везде, где это возможно. Если же бизнес-логика требует передачи таблиц между вызовами, используйте метод программной отсечки описания или ручное описание полей в конструкторе при использовании внешних источников данных. И не забывайте про индексацию временных таблиц по полям связи — это критически важно для производительности ваших запросов!