Как удалить записи из регистра сведений за определенный период?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе эксплуатации систем на базе 1С:Предприятие часто возникает задача универсальной очистки базы и регистров сведений от устаревших или некорректных данных. Если регистр является независимым и периодическим, удаление за конкретный период требует особого подхода, так как стандартные механизмы отбора в НаборЗаписей не поддерживают виды сравнения типа "Интервал".

В этой статье мы подробно разберем несколько проверенных способов решения этой задачи: от классических методов обхода выборки до современных высокопроизводительных функций платформы.

Важное разграничение: типы регистров и удаление движений документов

Перед тем как приступать к написанию кода, выясним тип регистра. Если регистр подчинен регистратору, удалять записи напрямую из него нельзя — это нарушит логику системы (за исключением случаев очистки записей по битым документам). В этом случае необходимо очищать движения самих документов. Если же регистр независимый, мы можем использовать программные методы удаления, которые рассмотрим ниже.

Способ 1. Использование Менеджера записи в цикле выборки

Этот метод считается наиболее простым и понятным. Мы получаем выборку записей за нужный период и для каждой записи вызываем Удалить() через её менеджер. Рассмотрим этот процесс по шагам:

  1. Создаем выборку из регистра, указав начало и конец периода.
  2. В цикле получаем менеджер для каждой записи.
  3. Вызываем метод удаления.

Выборка = РегистрыСведений.ИмяВашегоРегистра.Выбрать(НачалоПериода, КонецПериода);
Пока Выборка.Следующий() Цикл
    МенеджерЗаписи = Выборка.ПолучитьМенеджерЗаписи();
    МенеджерЗаписи.Удалить();
КонецЦикла;

Плюсы: Простота реализации, отсутствие необходимости вручную заполнять измерения для поиска записи.
Минусы: Низкая производительность при удалении тысяч или миллионов строк, так как каждая транзакция удаления выполняется отдельно.

Способ 2. Удаление через Набор записей с полной перезаписью

Если нам нужно удалить значительную часть записей (например, все записи за конкретный день), эффективнее использовать НаборЗаписей. Однако помните: у объекта Отбор в наборе записей для поля Период можно установить только вид сравнения Равно.

Проанализируем ситуацию: если наш регистр имеет периодичность "День", мы можем в цикле перебирать даты и очищать каждый день по отдельности.


ТекущаяДатаУдаления = НачалоДня(НачалоПериода);
Пока ТекущаяДатаУдаления <= КонецПериода Цикл
    НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Период.Установить(ТекущаяДатаУдаления);
    
    // Записываем пустой набор, что фактически удаляет все записи на эту дату
    НаборЗаписей.Записать(Истина); 
    
    ТекущаяДатаУдаления = ТекущаяДатаУдаления + 86400; // Переход к следующему дню
КонецЦикла;

Внимание: Если вы забудете установить отбор и вызовете Записать(Истина), платформа полностью очистит весь регистр. Всегда проверяйте наличие установленного отбора перед записью!

Способ 3. Удаление из прочитанного набора (обход ошибки итератора)

Часто программисты пытаются прочитать большой набор записей с отбором по измерениям, а затем удалить записи по условию периода в цикле Для каждого. Здесь кроется ловушка: удаление элемента из коллекции во время ее обхода приводит к пропуску следующей записи.

Рассмотрим правильный алгоритм с использованием промежуточного массива:


НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
// Устанавливаем фильтры по измерениям, если нужно сократить выборку
НаборЗаписей.Отбор.Организация.Установить(ВыбраннаяОрганизация);
НаборЗаписей.Прочитать();

МассивНаУдаление = Новый Массив;

// Сначала находим все подходящие записи
Для Каждого Запись Из НаборЗаписей Цикл
    Если Запись.Период >= НачалоПериода И Запись.Период <= КонецПериода Тогда
        МассивНаУдаление.Добавить(Запись);
    КонецЕсли;
КонецЦикла;

// Затем удаляем их из набора
Для Каждого ЗаписьДляУдаления Из МассивНаУдаление Цикл
    НаборЗаписей.Удалить(ЗаписьДляУдаления);
КонецЦикла;

НаборЗаписей.Записать(Истина);

Способ 4. Современный метод УдалитьЗаписи() (Платформа 8.3.23+)

Выясним приятную новость: в последних версиях платформы (начиная с 8.3.23.1865) появился специализированный метод, который работает на стороне СУБД крайне быстро. Теперь нам не нужно использовать циклы для удаления данных за период.

Синтаксис выглядит следующим образом:


СтруктураОтбора = Новый Структура;
// Для периодических регистров теперь можно передавать диапазон
СтруктураОтбора.Вставить("Период", Новый Диапазон(НачалоПериода, КонецПериода));

РегистрыСведений.ИмяВашегоРегистра.УдалитьЗаписи(СтруктураОтбора);

Это самый рекомендуемый способ, если версия вашей платформы позволяет его использовать. Он минимизирует количество блокировок и нагрузку на сервер.

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

Если вам необходимо удалить миллионы записей в высоконагруженной базе, выполнение операции в одной транзакции может привести к разрастанию лога транзакций СУБД и блокировке работы пользователей. В таких случаях мы рекомендуем удалять данные порциями.

Посмотрим на примерную схему работы:

  1. Запросом выбираем ПЕРВЫЕ 1000 записей, попадающих в период.
  2. Удаляем их одним из вышеописанных способов.
  3. Фиксируем транзакцию.
  4. Повторяем до тех пор, пока запрос возвращает данные.

Такой подход позволит системе "дышать" в перерывах между удалениями порций данных.

Резюме и рекомендации

Проанализировав все способы, можно сделать следующие выводы:

Используйте эти знания аккуратно, и ваши базы данных всегда будут содержать только актуальную информацию!

← На главную