Временные таблицы в 1С:Предприятие — это мощный инструмент для работы с промежуточными данными, который позволяет ускорить обработку больших массивов информации без нагрузки на основную базу. Однако ручное создание, управление и очистка таких таблиц часто превращается в хаос: забытые объекты занимают память, а неконтролируемое использование приводит к ошибкам блокировок. Решение — менеджер временных таблиц, который автоматизирует их жизненный цикл.

Эта статья не просто объяснит, как написать такой менеджер, но и раскроет скрытые нюансы работы с временными таблицами в 1С 8.3, которые редко упоминают в стандартной документации. Мы разберём архитектуру решения, типичные ошибки (включая те, что приводят к падению производительности на 300+ пользователей), и покажем, как интегрировать менеджер в существующие обработки без рефакторинга. Готовы оптимизировать свой код?

Зачем нужен менеджер временных таблиц в 1С

Временные таблицы в создаются для:

  • 📊 Хранения промежуточных результатов сложных отчётов (например, сводных данных по нескольким регистрам)
  • 🔄 Буферизации данных при обмене между системами (чтобы не блокировать основные таблицы)
  • ⚡ Ускорения массовых операций (вставка 100к строк в основную таблицу через временную работает в 3-5 раз быстрее)
  • 🔒 Изоляции транзакций (когда нужно гарантировать целостность данных при параллельных сеансах)

Но без контроля временные таблицы становятся тихими убийцами производительности:

  • 🧹 Забытые таблицы занимают память сервера и увеличивают время резервного копирования
  • 🔄 Дублирование имён приводит к коллизиям в многопользовательском режиме
  • 🐢 Неоптимизированные запросы к временным таблицам тормозят всю систему

Менеджер решает эти проблемы, предоставляя:

⚠️ Внимание: В клиент-серверном варианте 1С временные таблицы создаются в контексте сеанса. Это означает, что таблица, созданная в одном сеансе, не видна в другом — даже если у них одинаковые имена. Менеджер должен учитывать эту особенность при очистке!
📊 Как вы обычно работаете с временными таблицами в 1С?
Создаю вручную и очищаю по завершении
Использую самописный менеджер
Полагаюсь на автоматическую очистку 1С
Не использую временные таблицы

Архитектура менеджера: ключевые компоненты

Эффективный менеджер временных таблиц состоит из 4 обязательных слоёв:

Компонент Назначение Пример реализации
Фабрика имён Генерирует уникальные имена таблиц с учётом сеанса, даты и цели использования ТемпТаблица_Сеанс{УникальныйИдентификатор}_Дата{ГГГГММДД}
Репозиторий Хранит метаданные созданных таблиц (время создания, владелец, приоритет очистки) Структура или регистр сведений с полями ИмяТаблицы, ВремяСоздания, Сеанс, МодульВладелец
Монитор ресурсов Отслеживает использование памяти и предупреждает о превышении лимитов Запуск фонового процесса через Планировщик с проверкой каждые 5 минут
Очиститель Удаляет таблицы по правилам (по времени, приоритету или явному запросу) Метод ОчиститьТаблицыСтарше(Часы) с транзакционной безопасностью

Критическая особенность: менеджер должен работать и в тонком клиенте, и на сервере. Для этого:

  • 🖥️ Серверные методы (создание/очистка) помечайте директивой &НаСервере
  • 📱 Клиентские вызовы оборачивайте в Попытка...Исключение (на случай обрыва связи)
💡

Используйте префиксы имён таблиц по модулям (например, Отчет_, Обмен_, Обработка_). Это упростит отладку и позволит массово очищать таблицы определенного типа.

Пошаговая реализация менеджера

Начнём с базовой версии менеджера, которую можно расширять. Весь код размещаем в общем модуле с свойством Глобальный и правом Вызов сервера:

Перем мРепозиторийТаблиц; // Структура для хранения метаданных

Процедура ИнициализироватьМенеджер() Экспорт

мРепозиторийТаблиц = Новый Структура();

КонецПроцедуры

// Генерация уникального имени с учётом сеанса

Функция СгенерироватьИмяТаблицы(Префикс = "", Дополнительно = "") Экспорт

УникальныйИД = Строка(Новый УникальныйИдентификатор());

СеансИД = ТекущийСеанс(); // Работает только на сервере!

Возврат ?(Префикс <> "", Префикс + "_", "") +

"Темп_" + Формат(ТекущаяДата(), "ДФ=yyyyMMdd") + "_" +

СеансИД + "_" + Сред(УникальныйИД, 1, 8) +

?(Дополнительно <> "", "_" + Дополнительно, "");

КонецФункции

Теперь добавим методы для создания и очистки:

// Создание таблицы с регистрацией в репозитории

Функция СоздатьТаблицу(Префикс = "", СтруктураКолонок, Дополнительно = "") Экспорт

ИмяТаблицы = СгенерироватьИмяТаблицы(Префикс, Дополнительно);

// Создаём таблицу значений

Таблица = Новый ТаблицаЗначений;

Для Каждого Колонка Из СтруктураКолонок Цикл

Таблица.Колонки.Добавить(Колонка.Ключ, Колонка.Значение);

КонецЦикла;

// Регистрируем в репозитории

мРепозиторийТаблиц.Вставить(ИмяТаблицы, Новый Структура("ВремяСоздания,Сеанс,Модуль",

ТекущаяДата(), ТекущийСеанс(), ТекущийМодуль()));

Возврат Таблица;

КонецФункции

// Очистка по времени жизни (в часах)

Процедура ОчиститьСтарше(Часы = 24) Экспорт

Граница = ТекущаяДата() - Часы*3600;

Для Каждого Запись Из мРепозиторийТаблиц Цикл

Если Запись.Значение.ВремяСоздания < Граница Тогда

Попытка

Выполнить("УничтожитьОбъект(" + Запись.Ключ + ")");

Исключение

ЗаписьВЛог(НСтр("ru = 'Ошибка при очистке таблицы ''%1'': %2'"),

Запись.Ключ, ОписаниеОшибки());

КонецПопытки;

мРепозиторийТаблиц.Удалить(Запись.Ключ);

КонецЕсли;

КонецЦикла;

КонецПроцедуры

☑️ Минимальный набор методов для менеджера

Выполнено: 0 / 5

Оптимизация производительности

Без оптимизации менеджер может сам стать источником тормозов. Вот критические моменты, которые упускают 90% разработчиков:

  1. Индексы временных таблиц:

    Если вы создаёте таблицу для соединения с основными данными, обязательно добавьте индексы по полям соединения. Пример:

    Таблица.Индексы.Добавить("ИндексПоКоду", Новый ИндексКолонок("КодКонтрагента", Истина));

    Это ускорит запросы в 10-100 раз при работе с большими объёмами (100к+ строк).

  2. Транзакционная безопасность:

    Очистка таблиц должна выполняться в отдельной транзакции, иначе при откате основной операции "висячие" таблицы останутся в памяти:

    НачатьТранзакцию();
    

    Попытка

    ОчиститьСтарше(2);

    ЗафиксироватьТранзакцию();

    Исключение

    ОтменитьТранзакцию();

    ВызватьИсключение;

    КонецПопытки;

  3. Контроль памяти:

    В клиент-серверном варианте временные таблицы хранятся в RAM. Превышение лимита (обычно 1-2 Гб на сеанс) приводит к свапу на диск и падению производительности. Отслеживайте использование через:

    Если ПолучатьОбщуюИнформациюОСистеме().Память.Использовано > 0.8 Тогда
    

    ОчиститьСтарше(1); // Агрессивная очистка при 80% загрузки

    КонецЕсли;

⚠️ Внимание: В 1С:Предприятие 8.3.20+ появился механизм автоматической очистки временных таблиц при завершении сеанса, но он не работает если таблица была создана в транзакции, которая не завершилась (например, при ошибке). Всегда реализуйте резервный механизм очистки!
Как проверить реальное использование памяти временными таблицами?

Используйте запрос к системным таблицам (только для администраторов):

ВЫБРАТЬ

ИмяОбъекта КАК Таблица,

Размер КАК РазмерВБайтах

ИЗ

v8tempdb..sysobjects (NOLOCK)

ГДЕ

type = 'U' И name LIKE 'Темп_%'

Важно: Этот запрос работает только на MS SQL Server и требует прав sysadmin.

Типичные ошибки и как их избежать

Даже опытные разработчики допускают ошибки при работе с временными таблицами. Вот топ-5 проблем и их решения:

Ошибка Причина Решение
Ошибка блокировки SQLDeadlock Параллельные сеансы пытаются изменить одну временную таблицу (да, это возможно при одинаковых именах!) Всегда используйте уникальные имена с привязкой к сеансу: ТекущийСеанс() в имени
Падение производительности при 100+ пользователях Слишком много мелких временных таблиц (по 10-20 строк) создают нагрузку на ядро СУБД Объединяйте данные в одну таблицу с полем-разделителем (например, ТипДанных)
"Висячие" таблицы после ошибок Исключение прервало выполнение до очистки Используйте Попытка...Исключение с очисткой в блоке Исключение
Ошибка Таблица не найдена при запросе Таблица была очищена фоновым процессом во время выполнения запроса Помечайте активные таблицы флагом Используется = Истина и очищайте только неиспользуемые

Особая категория ошибок связана с репликацией данных в распределённых базах. Если ваша конфигурация использует РИБ, временные таблицы не реплицируются, но могут конфликтовать при синхронизации. Решение:

  • 🔗 Добавляйте в имя таблицы идентификатор узла РИБ: Узел_ + УникальныйИдентификаторУзла()
  • 🚫 Избегайте использования временных таблиц в общих модулях, которые выполняются на разных узлах
💡

Самая опасная ошибка — игнорирование транзакций при очистке. Если таблица была создана в транзакции, которая потом откатилась, а очистка прошла без транзакционного контроля, вы получите "зомби-таблицы", которые будут висеть в памяти до перезапуска сервера.

Интеграция с существующим кодом

Одной из главных проблем при внедрении менеджера является совместимость с legacy-кодом. Вот стратегия поэтапной интеграции:

  1. Аудит существующих временных таблиц:

    Найдите все места, где создаются таблицы через Новый ТаблицаЗначений или СоздатьОбъект("ТаблицаЗначений"). Используйте поиск по коду:

    ПоискПоТекстам.Найти("Новый ТаблицаЗначений", Истина);
    

    ПоискПоТекстам.Найти("СоздатьОбъект.*Таблица", Истина);

  2. Замена на вызов менеджера:

    Меняйте старый код:

    Таблица = Новый ТаблицаЗначений;
    

    Таблица.Колонки.Добавить("Поле1", Новый ОписаниеТипов("Строка"));

    На новый:

    Таблица = МенеджерВременныхТаблиц.СоздатьТаблицу("Отчет",
    

    Новый Структура("Поле1", Новый ОписаниеТипов("Строка")));

  3. Обработка "упрямых" мест:

    Если код использует динамическое создание таблиц (например, через Выполнить()), оберните его в прокси-функцию:

    Функция БезопасноеСозданиеТаблицы(Код)
    

    Попытка

    Результат = Выполнить(Код);

    Если ТипЗнч(Результат) = Тип("ТаблицаЗначений") Тогда

    МенеджерВременныхТаблиц.ЗарегистрироватьТаблицу(Результат, "Динамическая");

    КонецЕсли;

    Возврат Результат;

    Исключение

    ВызватьИсключение;

    КонецПопытки;

    КонецФункции

Для крупных проектов рекомендуем использовать гибридный подход:

  • 🔧 Новый код пишется с использованием менеджера
  • 🔄 Старый код постепенно рефакторится (по 1-2 модуля в неделю)
  • 🛡️ Критические участки (отчёты, обмены) переводим в первую очередь

Расширенные возможности менеджера

Базовая версия менеджера решает 80% задач, но для сложных систем потребуются дополнительные функции:

  1. Логирование операций:

    Добавьте запись в журнал регистрации при создании/очистке таблиц. Это поможет отследить утечки:

    Процедура ЗаписатьВЛог(Сообщение, Уровень = УровеньЖурнала.Информация) Экспорт
    

    ЖурналРегистрации.Записать(Сообщение,

    Уровень,

    , ,

    "МенеджерВременныхТаблиц");

    КонецПроцедуры

    Пример использования:

    МенеджерВременныхТаблиц.ЗаписатьВЛог(НСтр("ru = 'Создана таблица %1 (размер: %2 байт)'"),
    

    ИмяТаблицы, Таблица.ВыгрузитьВБуфер().Размер());

  2. Контроль версий структуры:

    Если структура временной таблицы меняется в процессе работы (добавляются/удаляются колонки), сохраняйте её версии:

    Процедура СохранитьСтруктуру(ИмяТаблицы, СтруктураКолонок)
    

    мРепозиторийТаблиц[ИмяТаблицы].Вставить("Структура", СтруктураКолонок);

    мРепозиторийТаблиц[ИмяТаблицы].Вставить("ВерсияСтруктуры",

    мРепозиторийТаблиц[ИмяТаблицы].Структура.ВерсияСтруктуры + 1);

    КонецПроцедуры

  3. Поддержка распределённых транзакций:

    Для систем с MS DTC или оркестрацией саг добавьте методы привязки таблиц к транзакциям:

    Процедура ПривязатьКТранзакции(ИмяТаблицы, ИДТранзакции)
    

    мРепозиторийТаблиц[ИмяТаблицы].Вставить("Транзакция", ИДТранзакции);

    КонецПроцедуры

    Процедура ОчиститьПоТранзакции(ИДТранзакции)

    Для Каждого Запись Из мРепозиторийТаблиц Цикл

    Если Запись.Значение.Свойство("Транзакция") = ИДТранзакции Тогда

    УничтожитьОбъект(Запись.Ключ);

    мРепозиторийТаблиц.Удалить(Запись.Ключ);

    КонецЕсли;

    КонецЦикла;

    КонецПроцедуры

Для промышленной эксплуатации также полезно добавить:

  • 📊 Статистику использования (сколько таблиц создаёт каждый модуль)
  • ⏱️ Тайм-ауты для долгоживущих таблиц (автоматическое удаление через X часов)
  • 🔒 Контроль доступа (разрешать создание таблиц только определённым ролям)

Тестирование и отладка

Перед внедрением менеджера в продакшн обязательно проведите стресс-тестирование. Вот чек-лист критичных сценариев:

☑️ Сценарии для тестирования менеджера

Выполнено: 0 / 5

Для автоматического тестирования используйте Тестер (встроенный в 1С) или xUnitFor1C. Пример теста:

&НаСервере

Процедура ТестСозданиеИОчистка()

// Arrange

МенеджерВременныхТаблиц.ИнициализироватьМенеджер();

СтруктураКолонок = Новый Структура("Поле1,Поле2", Новый ОписаниеТипов("Число"), Новый ОписаниеТипов("Строка"));

// Act

Таблица1 = МенеджерВременныхТаблиц.СоздатьТаблицу("Тест", СтруктураКолонок, "Тест1");

Таблица2 = МенеджерВременныхТаблиц.СоздатьТаблицу("Тест", СтруктураКолонок, "Тест2");

// Assert

Если МенеджерВременныхТаблиц.ПолучитьКоличествоТаблиц() <> 2 Тогда

ВызватьИсключение "Ожидалось 2 таблицы, найдено: " + МенеджерВременныхТаблиц.ПолучитьКоличествоТаблиц();

КонецЕсли;

// Cleanup

МенеджерВременныхТаблиц.ОчиститьВсе();

Если МенеджерВременныхТаблиц.ПолучитьКоличествоТаблиц() <> 0 Тогда

ВызватьИсключение "После очистки должно быть 0 таблиц";

КонецЕсли;

КонецПроцедуры

Для диагностики проблем в продакшне используйте:

  • 🔍 Журнал регистрации (фильтр по источнику МенеджерВременныхТаблиц)
  • 📈 Монитор производительности (включите сбор статистики по временным таблицам в Плановщик)
  • 🛠️ SQL Profiler (для MS SQL — отслеживайте запросы к tempdb)
⚠️ Внимание: В PostgreSQL временные таблицы 1С создаются в схеме pg_temp, а не в tempdb как в MS SQL. Это влияет на мониторинг и очистку! Для PostgreSQL добавьте в менеджер специальный метод проверки через запрос:
ВЫБРАТЬ schema_name, table_name

ИЗ information_schema.tables

ГДЕ table_schema = 'pg_temp'

FAQ: Частые вопросы по менеджеру временных таблиц

Можно ли использовать менеджер в управляемых формах?

Да, но с оговорками:

  • Создание таблиц должно выполняться на сервере (директива &НаСервере)
  • Для вызова с клиента используйте ВыполнитьНаСервере():
&НаКлиенте

Процедура СоздатьТаблицуНаСервере()

Таблица = ВыполнитьНаСервере("МенеджерВременныхТаблиц.СоздатьТаблицу",

"Отчет", Новый Структура("Поле1", Новый ОписаниеТипов("Число")));

КонецПроцедуры

Внимание: при обрыве связи клиент не узнает об ошибке создания таблицы!

Как менеджер ведёт себя при кластерном развёртывании 1С?

В кластере временные таблицы не реплицируются между рабочими процессами. Это означает:

  • Таблица, созданная на одном узле, не видна на другом
  • Очистка должна выполняться на каждом узле отдельно
  • Имена таблиц должны включать идентификатор узла: Узел_ + ТекущийУзелКластера()

Для получения текущего узла используйте:

Функция ТекущийУзелКластера()

Возврат СокрЛП(Строка(ПолучатьОбщуюИнформациюОСистеме().Кластер.ТекущийУзел));

КонецФункции

Что делать, если менеджер "съедает" слишком много памяти?

Проблема обычно в одном из трёх:

  1. Слишком длинные имена таблиц (ограничьте до 50 символов)
  2. Неконтролируемый рост данных (добавьте лимит на размер таблицы, например, 100 Мб)
  3. Утечки при исключениях (оберните создание таблиц в Попытка...Исключение)

Добавьте в менеджер метод аварийной очистки:

Процедура АварийнаяОчистка(ПроцентЗанятойПамяти = 90) Экспорт

Если ПолучатьОбщуюИнформациюОСистеме().Память.Использовано > ПроцентЗанятойПамяти Тогда

ОчиститьСтарше(0.1); // Удаляем все таблицы старше 6 минут

КонецЕсли;

КонецПроцедуры

Как мигрировать данные между временными таблицами разных сеансов?

Прямой обмен невозможен, но есть 3 обходных пути:

  1. Через XML/JSON:
    Данные = Таблица1.Выгрузить();
    

    Таблица2.Загрузить(Данные);

    Минус: медленно для больших объёмов (>10к строк).

  2. Через общую таблицу в базе:

    Создайте постоянную таблицу с префиксом Обмен_ и используйте её как буфер.

  3. Через файл:
    Таблица1.Записать("C:\Temp\buffer.dat");
    

    Таблица2.Прочитать("C:\Temp\buffer.dat");

    Минус: не работает в веб-клиенте.

Лучший вариант зависит от объёма данных и архитектуры системы.

Можно ли использовать менеджер в облачной версии 1С (1C:Fresh)?

Да, но с ограничениями:

  • ✅ Работают все базовые функции (создание, очистка)
  • ❌ Нет доступа к ПолучатьОбщуюИнформациюОСистеме() для мониторинга памяти
  • ❌ Ограничен доступ к системным таблицам SQL для диагностики

Рекомендации для 1C:Fresh:

  • Используйте агрессивную очистку (например, ОчиститьСтарше(0.5) — удалять таблицы старше 30 минут)
  • Отключите логирование (чтобы не перегружать журнал регистрации)
  • Тестируйте нагрузку — в облаке лимиты памяти жёстче, чем на локальном сервере