При работе с прямыми запросами в 1С 7.7 через внешнюю компоненту 1С++ и класс ODBCRecordset часто возникает необходимость использования временных таблиц. Это мощный инструмент для оптимизации сложных расчетов, однако он требует внимательного отношения к жизненному циклу объектов в базе данных tempdb. В данной статье мы подробно разберем, как создавать, использовать и, самое главное, корректно закрывать временные таблицы, чтобы избежать ошибок блокировок и переполнения памяти сервера.
Прежде всего, проанализируем, какие бывают временные таблицы и сколько они «живут» в контексте сессии 1С. В Microsoft SQL Server существует два основных типа временных таблиц, которые можно использовать через ODBCRecordset:
#VT. Такая таблица видна только в рамках того соединения (сеанса), в котором она была создана. Рассмотрим важный нюанс: в 1С++ таблица будет существовать до тех пор, пока не будет явно удалена или пока не закроется соединение ODBCRecordset. Если вы используете один и тот же объект запроса многократно, таблица останется в памяти tempdb.##VT. Они видны всем активным соединениям на сервере. Такая таблица удаляется только тогда, когда закрывается создавшее её соединение и к ней перестают обращаться все остальные сессии. Использовать их стоит с осторожностью, так как при одновременной работе нескольких пользователей велик риск конфликта имен (ошибка «В базе данных уже существует объект с именем...»).Многие начинающие разработчики пытаются использовать команду DELETE #VT, однако это в корне неверно. Команда DELETE удаляет строки из таблицы, но сама структура таблицы остается в базе данных. Чтобы полностью уничтожить таблицу и освободить ресурсы, необходимо использовать команду DROP TABLE.
Рассмотрим стандартный сценарий. Если вы выполните код SELECT * INTO #VT FROM ... повторно в одном и том же сеансе без предварительного удаления, SQL Server выдаст ошибку, так как объект #VT уже существует. Поэтому «золотым правилом» является проверка на существование таблицы перед её созданием или после завершения работы с ней.
Разберем наиболее надежный способ удаления временной таблицы. Мы проверим наличие объекта в системной базе tempdb. Это предотвратит ошибки в случае, если таблица еще не была создана или уже была удалена ранее. Проанализируем следующий пример кода:
// Создаем объект запроса
Запрос = СоздатьОбъект("ODBCRecordset");
// Текст для безопасного удаления
ТекстУдаления = "
|IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id = object_id('tempdb..#VT'))
|DROP TABLE #VT";
// Выполняем удаление
Запрос.Выполнить(ТекстУдаления);
Обратите внимание, что метод Выполнить() используется для команд, которые не возвращают набор данных (INSERT, UPDATE, DROP), в то время как ВыполнитьИнструкцию() чаще применяется для получения ТаблицыЗначений из оператора SELECT.
Существует два подхода к наполнению временных таблиц. Рассмотрим их особенности:
Вариант 1: Использование SELECT INTO. Этот способ наиболее прост в написании, так как SQL Server автоматически создает структуру таблицы на основе типов данных исходного запроса.
ТекстЗапроса = "
|SELECT
| Спр.ID as [Элемент $Справочник.Номенклатура],
| Спр.Code as Код
|INTO #TempTable
|FROM $Справочник.Номенклатура as Спр
|WHERE Спр.IsFolder = 0";
Запрос.Выполнить(ТекстЗапроса);
Вариант 2: Использование CREATE TABLE. Это более профессиональный подход. Он позволяет четко определить типы данных, назначить первичные ключи (Primary Key) и создать индексы. Выясним причину, почему это лучше: для больших объемов данных индексы на временной таблице ускорят последующие соединения (JOIN) в разы.
// 1. Создаем структуру
Запрос.Выполнить("CREATE TABLE #TempTable (ID CHAR(9), Счётчик INT, PRIMARY KEY (ID))");
// 2. Наполняем данными
Запрос.Выполнить("INSERT INTO #TempTable (ID, Счётчик) SELECT ID, 1 FROM $Справочник.Клиенты");
При использовании временных таблиц в 1С++ часто возникает проблема с типизацией полей (когда 1С не понимает, что в колонке лежит ссылка на справочник или документ). Посмотрим на пример типичной ошибки: если вы типизируете поля внутри SELECT INTO, имена колонок в SQL могут стать длинными и неудобными.
Рекомендация: старайтесь выполнять помещение данных в ВТ без типизации (простыми идентификаторами), а типизацию добавлять уже в финальном запросе SELECT, который возвращает результат в 1С. Рассмотрим это на примере:
// Помещаем данные во временную таблицу (сырые данные SQL)
ТекстПомещения = "SELECT iddoc, iddocdef INTO #Docs FROM _1sjourn WHERE date_spec = :ВыбрДата";
Запрос.Выполнить(ТекстПомещения);
// Получаем данные в 1С с типизацией
ТекстВыборки = "
|SELECT
| iddoc [Документ $Документ],
| iddocdef Документ_вид
|FROM #Docs";
ТЗ = Запрос.ВыполнитьИнструкцию(ТекстВыборки);
ТЗ.ВыбратьСтроку();
Важный момент для документов: чтобы 1С++ корректно распознала документ неопределенного вида, необходимо выбирать два поля — идентификатор документа (iddoc) и его вид (iddocdef), причем поле с видом должно иметь суффикс _вид (в нижнем регистре), как показано в примере выше.
Иногда задача стоит наоборот: у нас уже есть сформированная ТаблицаЗначений в памяти 1С, и мы хотим использовать её внутри SQL-запроса. Для этого в 1С++ существует замечательный метод ВыполнитьSQL_ИзТЗ(). Проанализируем алгоритм действий:
CREATE TABLE с нужной структурой.INSERT INTO с параметрами (знаками вопроса).ВыполнитьSQL_ИзТЗ(), передавая туда нашу таблицу.
// Имеем ТЗ с колонками ТоварID и Кол
Запрос.Выполнить("CREATE TABLE #MyVTs (ItemID CHAR(9), Quantity INT)");
Запрос.Подготовить("INSERT INTO #MyVTs (ItemID, Quantity) VALUES (?, ?)");
Запрос.ВыполнитьSQL_ИзТЗ(ТЗ_Данные);
// Теперь можем использовать #MyVTs в сложных JOIN
ТекстИтог = "SELECT * FROM #MyVTs INNER JOIN $Справочник.Номенклатура as ТОВ ON ItemID = ТОВ.ID";
Результат = Запрос.ВыполнитьИнструкцию(ТекстИтог);
Разберем несколько практических советов, которые помогут сделать ваши запросы стабильнее:
Используйте SET NOCOUNT ON. В начале длинных скриптов со множеством временных таблиц полезно добавлять эту команду. Она отключает передачу клиенту сообщений о количестве обработанных строк, что немного ускоряет работу и предотвращает конфликты с методом NextRecordset().
Индексация. Если вы планируете делать JOIN с временной таблицей, в которой более 1000 строк, обязательно создайте индекс по полю связи. Без индекса SQL Server будет выполнять полное сканирование временной таблицы (Table Scan) для каждой строки основного запроса.
Очистка. Хотя временные таблицы удаляются при закрытии соединения, в циклах или при частом формировании отчетов без перезапуска программы лучше явно вызывать DROP TABLE. Это поможет избежать разрастания базы tempdb, что критично для серверов с ограниченным дисковым пространством.
Таким образом, использование временных таблиц в 1С++ — это эффективный метод обработки данных. Соблюдая правила удаления и типизации, вы сможете строить быстрые и надежные отчеты даже на очень больших объемах данных 1С 7.7.