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

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

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

Вариант 1: Чтение, очистка и запись (Методически неверный)

Рассмотрим первый вариант, который часто выбирают начинающие разработчики. Суть его заключается в том, чтобы сначала получить данные из базы, затем очистить коллекцию в памяти и записать пустой набор обратно.


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

Проанализируем ситуацию: почему этот код считается плохим? Когда мы вызываем метод Прочитать(), система генерирует SQL-запрос SELECT. Если в регистре по указанному измерению хранятся тысячи или десятки тысяч записей, все эти данные будут переданы с сервера СУБД на сервер 1С, сериализованы и загружены в оперативную память. Это создает избыточную нагрузку на сеть и процессор. После этого метод Очистить() просто удаляет эти данные из памяти сервера 1С, а Записать(Истина) удаляет их из базы данных. Таким образом, чтение было абсолютно бесполезным действием.

Вариант 2: Запись пустого набора (Рекомендуемый метод)

Правильный подход заключается в том, чтобы сразу сообщить системе о необходимости замещения данных по фильтру пустым набором, не запрашивая сами данные из базы.


Набор = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
Набор.Отбор.Измерение1.Установить(ЗначениеИзмерения);
Набор.Записать(Истина);

Разберем по шагам, что происходит в этом случае. Метод Записать(Истина) с установленным отбором и пустой коллекцией записей внутри объекта Набор дает команду платформе: "Удали все записи в таблице БД, которые соответствуют данному отбору". На уровне СУБД это превращается в эффективную команду DELETE ... WHERE .... Это работает максимально быстро и потребляет минимум ресурсов.

Почему второй вариант может «не срабатывать»?

Иногда разработчики жалуются, что второй вариант не удаляет записи, в то время как первый (с чтением) отрабатывает корректно. Рассмотрим подробнее причины этого странного поведения:

  1. Неполный или неверный отбор. Если в регистре несколько измерений, а вы установили отбор только по одному из них, система удалит все записи, попадающие под этот фильтр. Если вы ожидали удаления только конкретной строки, а она осталась — проверьте, все ли измерения ключевого фильтра были указаны.
  2. Параметр «Замещать». Если по ошибке вызвать Набор.Записать(Ложь), система попытается выполнить добавление записей (Insert). Поскольку набор пуст, ничего не добавится, но и старые записи удалены не будут. Для очистки всегда используйте Истина.
  3. Логика в подписках и модуле набора. В процедуре ПередЗаписью модуля набора записей может быть написан код, который проверяет количество записей в коллекции ЭтотОбъект. Если коллекция пуста (как во втором варианте), условие может сработать так, что запись будет отменена или логика пойдет по другому пути. Первый вариант наполняет коллекцию записями из базы, поэтому проверки на "пустоту" там проходят иначе.

Влияние на блокировки и транзакции

Одним из самых критических аргументов против использования Прочитать() перед удалением являются транзакционные блокировки в высоконагруженных системах. Выясним причину возникновения взаимных блокировок (Deadlocks):

Когда выполняется Прочитать(), СУБД накладывает S-блокировку (разделяемую) на считываемый диапазон данных. Когда следом вызывается Записать(), система пытается повысить эту блокировку до X-блокировки (исключительной). Если в этот же момент другой сеанс также читает эти данные для последующей записи, возникает ситуация, когда оба сеанса ждут снятия S-блокировок друг друга, чтобы наложить X-блокировку. Это классический тупик. Второй вариант (сразу Записать) сразу запрашивает исключительную блокировку, что значительно снижает вероятность возникновения конфликтов.

Особенности для подчиненных регистров

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


Набор = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(СсылкаНаДокумент);
Набор.Записать(Истина);

Использование режима загрузки данных

Если нам нужно очистить данные максимально быстро, игнорируя при этом проверки в модулях и подписки на события, мы можем использовать свойство ОбменДанными.Загрузка. Рассмотрим пример:


Набор = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
Набор.Отбор.Измерение1.Установить(Значение);
Набор.ОбменДанными.Загрузка = Истина;
Набор.Записать(Истина);

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

Удаление одиночной записи через Менеджер Записи

Если задача стоит не в массовой очистке по фильтру, а в удалении ровно одной конкретной записи, чьи значения всех измерений нам известны, более элегантным решением будет использование МенеджерЗаписи.


Менеджер = РегистрыСведений.МойРегистр.СоздатьМенеджерЗаписи();
Менеджер.Измерение1 = Значение1;
Менеджер.Измерение2 = Значение2;
// Заполняем остальные ключевые измерения
Менеджер.Удалить();

Этот метод методически чище для работы с единичными данными, так как он не требует создания объекта набора и работы с коллекциями.

Подведем итоги

Проанализировав все способы, мы можем сделать однозначный вывод: для эффективной очистки данных в 1С следует использовать запись пустого набора с установленным отбором без предварительного вызова метода Прочитать(). Это экономит память сервера, снижает нагрузку на сеть с СУБД и предотвращает возникновение опасных блокировок в многопользовательской среде. Чтение набора перед записью оправдано только в одном случае: если вам программно необходимо проанализировать старые данные (например, залогировать их) непосредственно перед их удалением.

← На главную