При работе с высоконагруженными системами или при быстром анализе данных и глубокой отладке запросов разработчики часто сталкиваются с особенностью платформы 1С:Предприятие 8.3 — кэшированием временных таблиц на уровне СУБД. Суть проблемы заключается в том, что даже если в коде 1С вы явно уничтожаете временную таблицу или менеджер временных таблиц, в MS SQL Server физическая таблица в tempdb может не удаляться. Вместо выполнения команды DROP TABLE, система выполняет TRUNCATE TABLE и сохраняет структуру для повторного использования в рамках того же соединения. Это делается для оптимизации и снижения нагрузки на системные таблицы tempdb, но иногда нам необходимо увидеть в трассировке именно создание новой таблицы глазами 1С-ника.
В этой статье мы подробно разберем в консоли запросов, почему это происходит, и рассмотрим несколько практических способов, позволяющих заставить систему создавать объект СУБД заново.
Проанализируем ситуацию на уровне СУБД. Начиная с версии 8.3, платформа 1С стремится максимально эффективно использовать планы запросов. Если СУБД видит, что выполняется запрос, создающий временную таблицу с идентичным набором типов данных и порядком колонок, она просто подставляет уже существующую в кэше пустую заготовку. На уровне профайлера SQL это выглядит так: при первом запуске мы видим CREATE TABLE #tt..., а при втором — тишина, хотя данные в таблицу успешно помещаются.
Выясним причину такого поведения. Это связано с механизмом Temp Table Caching в MS SQL Server. Когда пакет (batch) завершается, временные таблицы не удаляются физически, если они не содержат именованных ограничений или других специфических объектов. 1С поддерживает этот механизм, чтобы не тратить ресурсы на перекомпиляцию планов выполнения запросов, которые зависят от структуры этих таблиц.
Рассмотрим самый доступный метод, не требующий вмешательства в настройки сервера. Поскольку SQL Server кэширует таблицы на основе их метаданных (типов и длины колонок), изменение любого из этих параметров приведет к тому, что кэшированная таблица не подойдет, и СУБД будет вынуждена создать новую.
Посмотрим на пример кода в консоли запросов для управляемых форм, где мы используем фиктивную колонку с меняющейся длиной:
// Пример использования переменной длины для обхода кэша
ДлинаТекста = Формат(ТекущаяДата(), "ДФ=ss"); // Используем секунды для уникальности длины
ТекстЗапроса =
"ВЫБРАТЬ
| 2 КАК Поле1,
| ВЫРАЗИТЬ(""0"" КАК СТРОКА(" + ДлинаТекста + ")) КАК ФиктивноеПоле
|ПОМЕСТИТЬ аа";
В данном случае при каждом выполнении запроса (если секунды отличаются) SQL Server будет видеть новую структуру (например, NVARCHAR(12) вместо NVARCHAR(11)) и будет генерировать новую таблицу в tempdb. Это гарантированно отобразится в трассе СУБД как полноценная операция создания объекта.
Разберем еще один способ влияния на кэширование. Для SQL Server таблицы (Колонка1 INT, Колонка2 INT) и (Колонка2 INT, Колонка1 INT) — это разные структуры. Если ваша логика позволяет менять порядок следования полей в инструкции ПОМЕСТИТЬ, вы можете использовать это для форсирования пересоздания.
Также эффективно работает добавление колонки с типом, который редко переиспользуется, например, УникальныйИдентификатор, или чередование признака NULL / NOT NULL. Однако манипуляция с длиной строки (как в первом способе) остается самой простой и надежной.
Проанализируем ситуацию, когда нам нужно программно создать скрипт создания временной таблицы на основе уже имеющейся (например, для переноса данных между контекстами или для анализа структуры). Если вы используете прямые запросы к СУБД, можно воспользоваться системными таблицами tempdb.
Ниже представлен скрипт для MS SQL Server, который генерирует CREATE TABLE на основе существующей временной таблицы 1С:
USE tempdb;
DECLARE @table_name SYSNAME = '#tt4_имя_из_1С'; -- Укажите реальное имя из трассы
DECLARE @object_id INT;
SELECT @object_id = [object_id]
FROM tempdb.sys.tables
WHERE name LIKE @table_name + '%';
DECLARE @SQL NVARCHAR(MAX) = '';
SELECT @SQL = 'CREATE TABLE #NewTable (' + CHAR(13) +
STUFF((
SELECT CHAR(9) + ', [' + c.name + '] ' +
UPPER(tp.name) +
CASE
WHEN tp.name IN ('varchar', 'nvarchar', 'varbinary') THEN
'(' + CASE WHEN c.max_length = -1 THEN 'MAX' ELSE CAST(c.max_length AS VARCHAR(5)) END + ')'
WHEN tp.name = 'decimal' THEN
'(' + CAST(c.[precision] AS VARCHAR(5)) + ',' + CAST(c.scale AS VARCHAR(5)) + ')'
ELSE ''
END +
CASE WHEN c.is_nullable = 1 THEN ' NULL' ELSE ' NOT NULL' END + CHAR(13)
FROM sys.columns c
JOIN sys.types tp ON c.user_type_id = tp.user_type_id
WHERE c.[object_id] = @object_id
ORDER BY c.column_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, CHAR(9) + ' ')
+ ')';
PRINT @SQL;
Этот подход полезен системным администраторам и разработчикам при написании сложных инструментов мониторинга производительности.
Многие разработчики полагают, что создание индекса на временной таблице в 1С (через конструкцию ИНДЕКСИРОВАТЬ ПО...) заставит систему пересоздать таблицу. Проверим это утверждение. На практике, если структура таблицы и состав индексируемых полей не меняются, 1С и SQL Server успешно переиспользуют закэшированную схему вместе с индексами.
Важный момент: если вы хотите, чтобы индекс создавался "с нуля", вам необходимо либо изменить набор индексируемых полей, либо воспользоваться методами, описанными в первом способе (изменение типов колонок).
Подводя итог, выделим основные правила работы с временными таблицами в контексте их пересоздания:
ВЫРАЗИТЬ(... КАК СТРОКА(N)). Это самый быстрый способ увидеть CREATE TABLE в профайлере.RECOMPILE в прямых запросах, если это применимо.tempdb. В рабочей среде лучше доверять механизмам кэширования платформы 1С.Рассмотренные нами методы позволяют гибко управлять поведением СУБД, обеспечивая необходимую прозрачность при анализе работы сложных запросов в среде 1С:Предприятие.