В процессе разработки на платформе 1С:Предприятие часто возникает задача полной очистки справочника перед загрузкой новых данных. Когда количество элементов исчисляется десятками тысяч (например, 20 000 и более), стандартные методы удаления в цикле могут работать крайне медленно. В этой статье мы подробно проанализируем, почему возникает эта проблема, и рассмотрим несколько эффективных способов её решения.
Рассмотрим стандартный подход, который часто выбирают начинающие специалисты. В коде выполняется запрос к справочнику, получается выборка, и в цикле для каждой ссылки вызывается метод ПолучитьОбъект().Удалить(). Основная причина низкой скорости здесь заключается в методе ПолучитьОбъект(). Проанализируем ситуацию: для удаления 20 000 элементов система должна 20 000 раз обратиться к базе данных, чтобы считать полные данные каждого объекта в память сервера, и только после этого выполнить команду удаления. Если справочник имеет тяжелые табличные части или множество реквизитов, время выполнения операции возрастает в геометрической прогрессии.
Один из самых эффективных способов ускорить запись или удаление объекта — это использование системного свойства ОбменДанными.Загрузка. Рассмотрим подробнее, как это работает. При установке этого флага в значение Истина, платформа отключает выполнение прикладных проверок в модуле объекта (процедуры ПередЗаписью, ПриЗаписи, ПередУдалением) и, что не менее важно, подавляет работу большинства подписок на события.
Выясним, как изменить код для использования этого преимущества:
&НаСервере
Процедура ОчиститьСправочникОптимизировано()
Выборка = Справочники.ФедеральныеЛьготники.Выбрать();
Пока Выборка.Следующий() Цикл
Объект = Выборка.Ссылка.ПолучитьОбъект();
Если Объект <> Неопределено Тогда
Объект.ОбменДанными.Загрузка = Истина;
Объект.Удалить();
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Этот метод значительно сокращает накладные расходы сервера на выполнение бизнес-логики, которая в данном случае (при полной очистке) не имеет смысла.
Разберем вопрос использования транзакций. Работа с базой данных без явного открытия транзакции заставляет СУБД (MS SQL, PostgreSQL) фиксировать каждое удаление как отдельную операцию в логе транзакций. Это создает колоссальную нагрузку на дисковую подсистему. Однако слишком большая транзакция (на все 20 000 элементов) может привести к переполнению лога или блокировке всей таблицы справочника на длительное время.
Оптимальным решением будет удаление порциями (пакетами). Посмотрим на пример кода, реализующего этот подход:
&НаСервере
Процедура УдалитьПакетами()
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Справочник.ФедеральныеЛьготники");
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Счетчик = 0;
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
СпрОбъект = Выборка.Ссылка.ПолучитьОбъект();
Если СпрОбъект <> Неопределено Тогда
СпрОбъект.ОбменДанными.Загрузка = Истина;
СпрОбъект.Удалить();
Счетчик = Счетчик + 1;
КонецЕсли;
// Фиксируем транзакцию каждые 1000 элементов
Если Счетчик % 1000 = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
КонецПроцедуры
Такой подход обеспечивает баланс между скоростью работы и стабильностью системы, предотвращая чрезмерный рост файла лога транзакций.
Проанализируем ситуацию, упомянутую в обсуждении: что произойдет, если на удаляемые элементы есть ссылки в других документах или справочниках? При использовании метода Удалить() платформа 1С не проверяет наличие ссылок автоматически (в отличие от интерактивного удаления помеченных объектов). Если мы удалим элемент, на который ссылается документ, в этом документе вместо значения появится надпись "Объект не найден".
Если справочник используется только для временного отображения данных и гарантированно не связан с другими объектами системы, непосредственное удаление допустимо. В противном случае лучше использовать метод УстановитьПометкуУдаления(Истина), который работает быстрее, чем полное удаление, но оставляет возможность корректной очистки через стандартные механизмы контроля целостности.
Рассмотрим ситуацию с другой стороны. Если данные в справочнике "идут особняком", нигде не используются как ссылочный тип и нужны только для визуализации информации из файла, использование справочника может быть избыточным. Проанализируем преимущества РегистрСведений:
НаборЗаписей.Записать(Истина) с пустым набором мгновенно очищает таблицу на уровне СУБД.Ссылка, Код, Наименование, что делает таблицу "легче".Пример мгновенной очистки регистра:
Набор = РегистрыСведений.ФедеральныеЛьготникиДанные.СоздатьНаборЗаписей();
// Пустой набор при записи с параметром Истина затирает все данные
Набор.Записать(Истина);
Для 20 000 записей эта операция займет доли секунды, в то время как удаление из справочника в любом случае потребует нескольких минут.
Чтобы пользователь не видел "зависшее" окно программы во время удаления и загрузки, крайне рекомендуется выносить эти процедуры в Фоновое задание. Рассмотрим основные шаги:
ФоновыеЗадания.Выполнить() для запуска процесса.ПодключитьОбработчикОжидания для проверки статуса задания и оповещения пользователя о завершении.Это позволит пользователю продолжать работу с другими разделами программы, пока сервер обрабатывает большой массив данных.
Для опытных администраторов существует радикальный способ — выполнение команды TRUNCATE TABLE непосредственно в базе данных. Это мгновенная операция, которая полностью очищает таблицу. Однако мы не рекомендуем этот метод начинающим специалистам по нескольким причинам:
_Reference123), которое может измениться при реструктуризации._Reference123_VT456).Подводя итоги, выделим лучший алгоритм действий для быстрой очистки данных:
ОбменДанными.Загрузка = Истина.Следуя этим правилам, вы сможете сократить время обработки справочника с нескольких часов до нескольких минут или даже секунд.