В процессе эксплуатации систем на базе 1С:Предприятие часто возникает задача универсальной очистки базы и регистров сведений от устаревших или некорректных данных. Если регистр является независимым и периодическим, удаление за конкретный период требует особого подхода, так как стандартные механизмы отбора в НаборЗаписей не поддерживают виды сравнения типа "Интервал".
В этой статье мы подробно разберем несколько проверенных способов решения этой задачи: от классических методов обхода выборки до современных высокопроизводительных функций платформы.
Перед тем как приступать к написанию кода, выясним тип регистра. Если регистр подчинен регистратору, удалять записи напрямую из него нельзя — это нарушит логику системы (за исключением случаев очистки записей по битым документам). В этом случае необходимо очищать движения самих документов. Если же регистр независимый, мы можем использовать программные методы удаления, которые рассмотрим ниже.
Этот метод считается наиболее простым и понятным. Мы получаем выборку записей за нужный период и для каждой записи вызываем Удалить() через её менеджер. Рассмотрим этот процесс по шагам:
Выборка = РегистрыСведений.ИмяВашегоРегистра.Выбрать(НачалоПериода, КонецПериода);
Пока Выборка.Следующий() Цикл
МенеджерЗаписи = Выборка.ПолучитьМенеджерЗаписи();
МенеджерЗаписи.Удалить();
КонецЦикла;
Плюсы: Простота реализации, отсутствие необходимости вручную заполнять измерения для поиска записи.
Минусы: Низкая производительность при удалении тысяч или миллионов строк, так как каждая транзакция удаления выполняется отдельно.
Если нам нужно удалить значительную часть записей (например, все записи за конкретный день), эффективнее использовать НаборЗаписей. Однако помните: у объекта Отбор в наборе записей для поля Период можно установить только вид сравнения Равно.
Проанализируем ситуацию: если наш регистр имеет периодичность "День", мы можем в цикле перебирать даты и очищать каждый день по отдельности.
ТекущаяДатаУдаления = НачалоДня(НачалоПериода);
Пока ТекущаяДатаУдаления <= КонецПериода Цикл
НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Период.Установить(ТекущаяДатаУдаления);
// Записываем пустой набор, что фактически удаляет все записи на эту дату
НаборЗаписей.Записать(Истина);
ТекущаяДатаУдаления = ТекущаяДатаУдаления + 86400; // Переход к следующему дню
КонецЦикла;
Внимание: Если вы забудете установить отбор и вызовете Записать(Истина), платформа полностью очистит весь регистр. Всегда проверяйте наличие установленного отбора перед записью!
Часто программисты пытаются прочитать большой набор записей с отбором по измерениям, а затем удалить записи по условию периода в цикле Для каждого. Здесь кроется ловушка: удаление элемента из коллекции во время ее обхода приводит к пропуску следующей записи.
Рассмотрим правильный алгоритм с использованием промежуточного массива:
НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
// Устанавливаем фильтры по измерениям, если нужно сократить выборку
НаборЗаписей.Отбор.Организация.Установить(ВыбраннаяОрганизация);
НаборЗаписей.Прочитать();
МассивНаУдаление = Новый Массив;
// Сначала находим все подходящие записи
Для Каждого Запись Из НаборЗаписей Цикл
Если Запись.Период >= НачалоПериода И Запись.Период <= КонецПериода Тогда
МассивНаУдаление.Добавить(Запись);
КонецЕсли;
КонецЦикла;
// Затем удаляем их из набора
Для Каждого ЗаписьДляУдаления Из МассивНаУдаление Цикл
НаборЗаписей.Удалить(ЗаписьДляУдаления);
КонецЦикла;
НаборЗаписей.Записать(Истина);
Выясним приятную новость: в последних версиях платформы (начиная с 8.3.23.1865) появился специализированный метод, который работает на стороне СУБД крайне быстро. Теперь нам не нужно использовать циклы для удаления данных за период.
Синтаксис выглядит следующим образом:
СтруктураОтбора = Новый Структура;
// Для периодических регистров теперь можно передавать диапазон
СтруктураОтбора.Вставить("Период", Новый Диапазон(НачалоПериода, КонецПериода));
РегистрыСведений.ИмяВашегоРегистра.УдалитьЗаписи(СтруктураОтбора);
Это самый рекомендуемый способ, если версия вашей платформы позволяет его использовать. Он минимизирует количество блокировок и нагрузку на сервер.
Если вам необходимо удалить миллионы записей в высоконагруженной базе, выполнение операции в одной транзакции может привести к разрастанию лога транзакций СУБД и блокировке работы пользователей. В таких случаях мы рекомендуем удалять данные порциями.
Посмотрим на примерную схему работы:
ПЕРВЫЕ 1000 записей, попадающих в период.Такой подход позволит системе "дышать" в перерывах между удалениями порций данных.
Проанализировав все способы, можно сделать следующие выводы:
ПолучитьМенеджерЗаписи() в цикле выборки.НаборЗаписей с установленным отбором по дате.УдалитьЗаписи().Используйте эти знания аккуратно, и ваши базы данных всегда будут содержать только актуальную информацию!