При попытке полностью очистить крупный регистр сведений с миллионами записей стандартным кодом разработчики нередко сталкиваются с длительным зависанием сеанса. Процесс может продолжаться часами без видимой нагрузки на процессор. Разберем, почему возникает такая ситуация и как правильно выполнять массовое удаление данных в 1С.
Рассмотрим стандартный вызов очистки регистра:
НаборЗаписей = РегистрыСведений.ОбъектыИнформационныхБаз.СоздатьНаборЗаписей();
НаборЗаписей.Записать();
При выполнении этого кода для таблицы объемом в несколько миллионов строк платформа 1С и СУБД выполняют огромный объем сопутствующих операций:
_RecordChange). Часто удаление уже завершено на уровне основной таблицы, но сеанс продолжает ожидать окончания записи изменений для обмена.Если очистка регистра выполняется в рамках регламентных работ и передавать факт удаления в подчиненные узлы распределенной информационной базы (РИБ) не требуется, обязательно используем режим загрузки данных. Установка свойства ОбменДанными.Загрузка = Истина отключает платформенную авторегистрацию изменений и выполнение обработчиков ПередЗаписью и ПриЗаписи модуля набора записей.
Посмотрим на пример правильной записи пустого набора:
НаборЗаписей = РегистрыСведений.ОбъектыИнформационныхБаз.СоздатьНаборЗаписей();
НаборЗаписей.ОбменДанными.Загрузка = Истина;
НаборЗаписей.Записать();
Чтобы не блокировать таблицу целиком и не переполнять журнал транзакций СУБД, организуем удаление пакетами фиксированного размера. Для контроля процесса добавим вывод прогресса и метод ОбработкаПрерыванияПользователя(), позволяющий при необходимости прервать операцию комбинацией клавиш Ctrl + Break / Ctrl + C.
Разберем алгоритм порционной очистки с помощью запроса по ключевым полям:
РазмерПорции = 10000;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ " + Формат(РазмерПорции, "ЧГ=0") + "
| Т.УзелОбмена,
| Т.Ссылка
|ИЗ
| РегистрСведений.ОбъектыИнформационныхБаз КАК Т";
Пока Истина Цикл
ОбработкаПрерыванияПользователя();
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Количество() = 0 Тогда
Прервать;
КонецЕсли;
НачатьТранзакцию();
Попытка
Пока Выборка.Следующий() Цикл
Набор = РегистрыСведений.ОбъектыИнформационныхБаз.СоздатьНаборЗаписей();
Набор.Отбор.УзелОбмена.Установить(Выборка.УзелОбмена);
Набор.Отбор.Ссылка.Установить(Выборка.Ссылка);
Набор.ОбменДанными.Загрузка = Истина;
Набор.Записать();
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
КонецЦикла;
Если у регистра сведений есть ведущее измерение или регистр является периодическим, эффективнее всего очищать данные блоками, устанавливая отбор по каждому конкретному значению ведущего измерения или по диапазонам дат. В этом случае один вызов Записать() удалит сразу группу строк, относящихся к выбранному значению, без необходимости перебора каждой отдельной записи.
Для мгновенной очистки таблицы на стороне СУБД можно использовать команду TRUNCATE TABLE. Этот метод занимает считанные секунды даже на таблицах в десятки миллионов строк, так как СУБД освобождает страницы данных целиком без построчного удаления.
При использовании прямого SQL-запроса важно учитывать следующие критические правила:
ПолучитьСтруктуруХраненияБазыДанных()) и имя связанной таблицы изменений (префикс _RecordChange), после чего выполнить TRUNCATE TABLE для обеих таблиц. В противном случае в планах обмена останутся невалидные ссылки.После удаления миллионов записей база данных требует регламентного обслуживания, иначе последующие запросы к регистру могут выполняться крайне медленно: