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

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

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

1. Быстрая и оптимальная очистка регистра сведений по отбору

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


// Неоптимальный код!
Набор = РегистрыСведений.мд_ХарактеристикиГрупп.СоздатьНаборЗаписей();
Набор.Отбор.НоменклатурнаяГруппа.Установить(НоменклатурнаяГруппа);
Набор.Прочитать();
Набор.Очистить();
Набор.Записать();

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

Для удаления записей по отбору **достаточно записать пустой набор**. Метод Записать() с параметром замещения по умолчанию (Истина) автоматически удаляет из базы данных все существующие записи, попадающие под установленный отбор, и записывает на их место содержимое набора. А так как мы не заполняли набор данными, он запишется пустым, что и приведет к эффективному удалению.

Разберем по шагам правильный и максимально быстрый вариант реализации этой операции:


Набор = РегистрыСведений.мд_ХарактеристикиГрупп.СоздатьНаборЗаписей();
Набор.Отбор.НоменклатурнаяГруппа.Установить(НоменклатурнаяГруппа);
// Записываем пустой набор без предварительного чтения данных!
Набор.Записать(); 

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

2. Безопасная пометка связанных элементов справочника на удаление

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

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

Для решения этой задачи рассмотрим оптимальный алгоритм на основе запроса:

  1. Используем ключевое слово РАЗЛИЧНЫЕ в тексте запроса, чтобы получить строго уникальные ссылки на справочники и исключить дублирование при обходе.
  2. Перед модификацией объекта обязательно проверяем его текущее состояние. Если элемент уже помечен на удаление, перезаписывать его не нужно.
  3. Используем метод УстановитьПометкуУдаления(), который сразу записывает изменения в базу данных без необходимости вызывать метод Записать() вручную.

Посмотрим на пример программной реализации этой логики:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	Характеристики.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.Характеристики КАК Характеристики
    |ГДЕ
    |	Характеристики.НоменклатурнаяГруппа = &НоменклатурнаяГруппа";

Запрос.УстановитьПараметр("НоменклатурнаяГруппа", НоменклатурнаяГруппа);
Выборка = Запрос.Выполнить().Выбрать();

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

Важное предупреждение: Ни в коем случае не заменяйте пометку удаления на непосредственное физическое удаление методом Удалить(). Физическое удаление объектов из кода обходит контроль ссылочной целостности СУБД, из-за чего в связанных документах и регистрах появятся «битые ссылки» вида <Объект не найден...>, что нарушит логику работы базы данных.

3. Запись значений составного типа (Предопределенные элементы и Булево)

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

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

Посмотрим на пример работы с предопределенными элементами и булевым типом на стороне сервера:


// Запись логического значения Ложь
РесурсРегистра = Ложь;

// Обращение к предопределенному элементу на сервере
РесурсРегистра = Справочники.Характеристики.ПредопределенноеЗначение;

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


// Получение ссылки на предопределенный элемент на клиенте
ЗначениеДляЗаписи = ПредопределенноеЗначение("Справочник.Характеристики.ИмяПредопределенногоЭлемента");

4. Настройка отборов в списках (Обычные и Управляемые формы)

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

Если вы до сих пор поддерживаете решения на **Обычных формах** (платформа 8.2 или режим совместимости), отбор устанавливается напрямую через свойства коллекции элементов формы:


// Код для обычных форм
СправочникСписок.Отбор.НоменклатурнаяГруппа.Использование = Истина;
СправочникСписок.Отбор.НоменклатурнаяГруппа.Значение = ВыбраннаяГруппа;
СправочникСписок.Отбор.НоменклатурнаяГруппа.ВидСравнения = ВидСравнения.Равно;
ЭлементыФормы.СправочникСписок.НастройкаОтбора.НоменклатурнаяГруппа.Доступность = Ложь;

В современных конфигурациях на **Управляемых формах (платформа 8.3)** списки выводятся с помощью динамических списков, работающих на базе системы компоновки данных (СКД). Программная установка отбора здесь реализуется через настройки компоновщика:


// Код для управляемых форм (динамический список "Список")
ОтборСписок = Список.КомпоновщикНастроек.Настройки.Отбор.Элементы;

// Сначала найдем и удалим старый отбор по этому полю, чтобы избежать дублирования
Для Каждого ЭлементОтбора Из ОтборСписок Цикл
    Если ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("НоменклатурнаяГруппа") Тогда
        ОтборСписок.Удалить(ЭлементОтбора);
    КонецЕсли;
КонецЦикла;

// Добавляем новый элемент отбора
НовыйОтбор = ОтборСписок.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
НовыйОтбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("НоменклатурнаяГруппа");
НовыйОтбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
НовыйОтбор.ПравоеЗначение = ВыбраннаяГруппа;
НовыйОтбор.Использование = Истина;

Если в вашей конфигурации используется Библиотека стандартных подсистем (БСП), то этот громоздкий код можно заменить вызовом всего одной сервисной процедуры общего модуля:


// Использование стандартных функций БСП
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
    Список, 
    "НоменклатурнаяГруппа", 
    ВыбраннаяГруппа, 
    ВидСравненияКомпоновкиДанных.Равно
);

Также помните, что для интерактивного выбора на форме часто вообще не требуется писать код. В свойствах реквизита формы в конфигураторе можно настроить свойство «Связи параметров выбора», указав в качестве связи реквизит «НоменклатурнаяГруппа». Платформа 1С автоматически применит отбор при открытии формы выбора, полностью избавив вас от написания программного кода.

← На главную