При разработке решений в 1С мы часто сталкиваемся с необходимостью обработки больших объемов данных, хранящихся во временных таблицах значений (ТЗ) или табличных частях (ТЧ) объектов. Неправильное управление этими данными может привести к избыточному потреблению памяти и снижению производительности системы. В этой статье мы подробно рассмотрим, как эффективно очищать временные таблицы после использования и как правильно передавать табличные части объектов или обработок напрямую в запросы 1С, чтобы избежать лишних операций и оптимизировать работу с данными.
Давайте вместе разберем основные подходы и выясним, какие методы являются наиболее предпочтительными для различных сценариев.
Когда мы работаем с большими объемами данных, помещенными во временные таблицы значений, возникает вопрос об их очистке после того, как данные перестают быть нужными. Это особенно актуально, когда речь идет о десятках и сотнях тысяч строк, которые могут занимать значительный объем оперативной памяти сервера.
Присвоение переменной значения Неопределено:
Наиболее эффективным и правильным способом сообщить платформе 1С о том, что объект (в данном случае, таблица значений) больше не используется, является присвоение переменной, которая на него ссылается, значения Неопределено.
Например, если у нас есть переменная ДанныеДляЗапроса, которая хранит нашу таблицу значений, то после выполнения всех необходимых операций с этой таблицей, мы можем сделать следующее:
ДанныеДляЗапроса = Неопределено;
Что это дает: Когда мы присваиваем переменной Неопределено, мы фактически удаляем последнюю ссылку на объект таблицы значений в памяти. Как только на объект не останется ни одной ссылки, он становится доступным для сборщика мусора платформы 1С. Сборщик мусора запускается автоматически, когда система считает это необходимым (например, при нехватке памяти или в периоды простоя), и освобождает занимаемую объектом память.
Важно понимать: Освобождение памяти не происходит мгновенно. Это фоновый процесс. Однако, явно обнуляя ссылки, мы даем системе возможность более эффективно управлять ресурсами.
Метод ТЗ.Очистить():
Этот метод удаляет все строки из таблицы значений, но сам объект таблицы (с его описанием колонок) остается в памяти. Память, занимаемая данными строк, освободится, но переменная все еще будет ссылаться на существующий, хоть и пустой, объект таблицы. Если таблица значений больше не нужна в принципе, присвоение Неопределено является более предпочтительным, так как это удаляет ссылку на весь объект, позволяя сборщику мусора убрать его полностью.
Вывод: Присвоение переменной Неопределено является лучшей практикой для освобождения памяти, занимаемой большими временными таблицами значений, так как оно позволяет системе полностью удалить объект из памяти.
Часто встречается заблуждение, что табличную часть обработки или другого объекта невозможно напрямую передать в запрос, и для этого обязательно требуется предварительная выгрузка данных в промежуточную таблицу значений. Давайте разберемся, так ли это.
Возможность прямой передачи ТЧ:
На самом деле, табличную часть объекта или обработки можно передать в запрос 1С напрямую. Это стандартная и эффективная возможность платформы. В тексте запроса такая табличная часть будет доступна как обычный источник данных.
Причина возникновения ошибок: контекст выполнения:
Почему же у некоторых разработчиков возникают сложности или ошибки при попытке передать ТЧ напрямую? Причина кроется в контексте выполнения кода.
В модуле формы: Табличная часть, которая находится на форме (например, Элементы.МояТаблица.ТекущиеДанные или Объект.МояТабличнаяЧасть, если Объект - это реквизит формы типа ДанныеФормыСтруктура или ДанныеФормыКоллекция), является коллекцией элементов формы. Именно такую коллекцию напрямую передать в запрос нельзя – платформа выдаст ошибку.
Для того чтобы получить собственно объект ТабличнаяЧасть из данных формы, нам необходимо использовать метод РеквизитФормыВЗначение(). Этот метод преобразует данные формы в данные объекта.
// Пример в модуле формы обработки
Процедура ВыполнитьЗапрос(Команда)
Запрос = Новый Запрос;
// Получаем ТабличнуюЧасть объекта из реквизита формы "Объект"
// Предполагается, что на форме есть реквизит "Объект" типа "ОбработкаОбъект.<ИмяОбработки>"
// и у этого объекта есть табличная часть "МояТабличнаяЧасть"
ТабличнаяЧастьОбработки = РеквизитФормыВЗначение("Объект").МояТабличнаяЧасть;
Запрос.Текст =
"ВЫБРАТЬ
| ТабличнаяЧасть.Поле1,
| ТабличнаяЧасть.Поле2
|ИЗ
| &ТЧПараметр КАК ТабличнаяЧасть";
Запрос.УстановитьПараметр("ТЧПараметр", ТабличнаяЧастьОбработки);
Результат = Запрос.Выполнить().Выгрузить();
// Дальнейшая обработка результата
КонецПроцедуры
В серверном или общем модуле (на сервере): Когда код выполняется на сервере (например, в общем модуле, который вызывается с клиента), мы работаем непосредственно с серверными объектами. Если мы передаем в такую серверную процедуру табличную часть как параметр, она уже является полноценным объектом ТабличнаяЧасть, а не коллекцией формы. В этом случае ее можно передавать в запрос напрямую, без дополнительных преобразований.
// Пример в общем модуле (вызывается на сервере)
// Предполагается, что ТЧ_Входная уже является объектом типа "ТабличнаяЧасть"
Процедура ВыполнитьЗапросСервер(ТЧ_Входная) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| МояТаблица.Колонка1,
| МояТаблица.Колонка2
|ИЗ
| &ПараметрТЧ КАК МояТаблица";
Запрос.УстановитьПараметр("ПараметрТЧ", ТЧ_Входная);
Результат = Запрос.Выполнить().Выгрузить();
// Дальнейшая обработка результата
КонецПроцедуры
Производительность:
Прямая передача табличной части в запрос является более производительной и менее ресурсоемкой операцией, чем предварительная выгрузка в таблицу значений (ТЧ.Выгрузить()) и затем передача этой ТЗ. Почему?
ТабличнаяЧасть при передаче его в запрос, так как этот механизм является штатным и оптимизированным.Вывод: Всегда стремитесь передавать табличную часть в запрос напрямую. Если вы работаете в модуле формы, используйте РеквизитФормыВЗначение() для преобразования данных формы в объектную табличную часть. Это позволит избежать лишних затрат памяти и улучшит производительность.
В контексте работы с запросами и временными таблицами, часто упоминается инструкция УНИЧТОЖИТЬ. Давайте разберем ее назначение и когда ее следует применять.
Назначение инструкции УНИЧТОЖИТЬ:
Инструкция УНИЧТОЖИТЬ используется в пакетных запросах для явного удаления временной таблицы, которая была создана внутри самого запроса с помощью конструкции ПОМЕСТИТЬ. Ее основная задача — освободить ресурсы СУБД, занимаемые этой временной таблицей, сразу после того, как она перестает быть нужной в рамках текущего пакета запросов.
Пример использования ПОМЕСТИТЬ и УНИЧТОЖИТЬ:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Документ.Ссылка КАК Ссылка,
| Документ.Дата КАК Дата
|ПОМЕСТИТЬ ВремТабДокументов
|ИЗ
| Документ.ПоступлениеТоваровУслуг КАК Документ
|ГДЕ
| Документ.Проведен = ИСТИНА
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВремТабДокументов.Ссылка,
| ВремТабДокументов.Дата,
| СтрокаТЧ.Номенклатура,
| СтрокаТЧ.Количество
|ИЗ
| ВремТабДокументов КАК ВремТабДокументов
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг.Товары КАК СтрокаТЧ
| ПО ВремТабДокументов.Ссылка = СтрокаТЧ.Ссылка
|;
|
|////////////////////////////////////////////////////////////////////////////////
|УНИЧТОЖИТЬ ВремТабДокументов
|";
Результат = Запрос.Выполнить().Выгрузить();
В этом примере мы сначала создаем временную таблицу ВремТабДокументов, используем ее во втором пакете запроса, а затем явно уничтожаем, чтобы освободить ресурсы.
Неприменимость к внешним таблицам:
Инструкция УНИЧТОЖИТЬ не может быть применена к таблицам, которые были переданы в запрос извне в качестве параметра (например, УНИЧТОЖИТЬ &МояТаблица). Ее задача — управление жизненным циклом временных таблиц, созданных именно внутри запроса (через ПОМЕСТИТЬ), а не управление памятью переменных в коде 1С. Для внешних таблиц мы используем подход с присвоением Неопределено, о котором мы говорили ранее.
Вывод: Используйте УНИЧТОЖИТЬ для временных таблиц, созданных внутри пакетных запросов с помощью ПОМЕСТИТЬ, чтобы своевременно освобождать ресурсы СУБД. Этот механизм дополняет управление памятью объектов 1С.
Когда мы работаем с таблицами, содержащими десятки, а иногда и сотни тысяч строк, вопросы оптимизации становятся критически важными. Рассмотрим несколько ключевых аспектов, которые помогут нам улучшить производительность.
Индексация:
При передаче таблицы значений или табличной части в запрос в качестве параметра, критически важно создавать индексы для тех колонок, по которым будут происходить соединения (ВНУТРЕННЕЕ СОЕДИНЕНИЕ, ЛЕВОЕ СОЕДИНЕНИЕ) с другими таблицами или фильтрация. Отсутствие индексов при соединении больших таблиц является одной из самых частых причин очень низкой производительности запросов.
Как создать индекс:
// При создании новой ТаблицыЗначений
НоваяТЗ = Новый ТаблицаЗначений;
НоваяТЗ.Колонки.Добавить("КлючевоеПоле");
НоваяТЗ.Колонки.Добавить("ДругоеПоле");
НоваяТЗ.Индексы.Добавить("КлючевоеПоле"); // Создаем индекс по ключевому полю
// Для существующей ТаблицыЗначений или ТабличнойЧасти
Если НЕ ТаблицаДанных.Индексы.Найти("ИндексПоКлючу") Тогда
ТаблицаДанных.Индексы.Добавить("КлючевоеПоле", "ИндексПоКлючу");
КонецЕсли;
Почему это важно: Индексы значительно ускоряют поиск и соединение данных, так как СУБД не приходится выполнять полное сканирование всей таблицы. Это особенно заметно при работе с большим числом строк.
Пакетные запросы:
Если с данными необходимо произвести несколько последовательных манипуляций (фильтрация, соединение, агрегация, группировка), рекомендуется использовать пакетные запросы. Вместо того чтобы выполнять несколько отдельных запросов и гонять большие объемы данных между сервером 1С и СУБД для каждой промежуточной операции, мы можем передать исходную таблицу один раз, а все последующие преобразования выполнять внутри одного запроса с использованием временных таблиц (ПОМЕСТИТЬ).
Преимущества:
Общие соображения о производительности и памяти:
Объем данных: Десятки тысяч строк (например, до 50 000) для 1С обычно не являются критичным объемом, если запросы написаны эффективно и используются индексы. Однако, при сотнях тысяч и миллионах строк даже малейшие неоптимальности могут привести к серьезным проблемам с производительностью и потреблением памяти.
Серверное оборудование: На старом или слабо сконфигурированном оборудовании (сервера 10-15 летней давности) даже относительно небольшие объемы данных могут вызвать значительные задержки и повышенное потребление памяти. В таких условиях каждая копейка памяти и каждое сокращение процессорного времени становится критически важным.
Количество колонок: Чем больше колонок в табличной части или таблице значений, тем больше памяти занимает каждая строка. Старайтесь передавать и обрабатывать только те данные, которые действительно необходимы.
Вывод: Активное использование индексов, пакетных запросов и внимательное отношение к объему передаваемых данных — это фундамент для создания высокопроизводительных решений в 1С, особенно при работе с большими массивами информации.
Подводя итог, мы выяснили, что для эффективной работы с временными таблицами и табличными частями в 1С необходимо применять следующие практики:
Неопределено.РеквизитФормыВЗначение() в модулях форм или просто сам объект ТабличнаяЧасть в серверном коде.УНИЧТОЖИТЬ для временных таблиц, созданных внутри пакетных запросов.Следуя этим рекомендациям, мы сможем значительно улучшить производительность наших решений и более эффективно управлять системными ресурсами, особенно при работе с большим количеством данных.