Почему при установке отбора в наборе записей регистра сведений выбираются все записи?

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

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

Проанализируем типичную ситуацию на примере исходного кода:


Записи = РегистрыСведений.НастройкиПользователей.СоздатьНаборЗаписей();
Записи.Отбор.Пользователь.Значение = СсылкаНаЭлемент.Ссылка;
Записи.Отбор.Настройка.Значение = ПланыВидовХарактеристик.НастройкиПользователей.ОсновнаяОрганизация;
Если ЗначениеЗаполнено(ОсновнаяОрганизация) Тогда 
    Записи.Прочитать();    
    Для Каждого Запись Из Записи Цикл 
        Запись.Значение = ОсновнаяОрганизация;
    КонецЦикла;
    Записи.Записать();
КонецЕсли;

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

Причина проблемы: неактивное свойство Использование

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

Когда мы напрямую присваиваем значение свойству: Записи.Отбор.Пользователь.Значение = СсылкаНаЭлемент.Ссылка;, платформа лишь сохраняет значение во внутреннем буфере элемента отбора, но не активирует сам отбор. Поскольку флаг Использование остается равным Ложь, при вызове метода Прочитать() платформа считает, что никаких фильтров накладывать не требуется, и считывает из базы абсолютно все строки.

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

Способ 1. Использование метода Установить() (Рекомендуемый)

Самый надежный и лаконичный способ установить значение отбора — вызвать специальный метод Установить() у соответствующего элемента коллекции. Этот метод одновременно передает значение и переводит свойство Использование в состояние Истина.

Посмотрим на пример правильной инициализации отбора:


Записи = РегистрыСведений.НастройкиПользователей.СоздатьНаборЗаписей();
Записи.Отбор.Пользователь.Установить(СсылкаНаЭлемент.Ссылка);
Записи.Отбор.Настройка.Установить(ПланыВидовХарактеристик.НастройкиПользователей.ОсновнаяОрганизация);

Записи.Прочитать();

Способ 2. Явное включение свойства Использование

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


Записи = РегистрыСведений.НастройкиПользователей.СоздатьНаборЗаписей();

Записи.Отбор.Пользователь.Значение = СсылкаНаЭлемент.Ссылка;
Записи.Отбор.Пользователь.Использование = Истина;

Записи.Отбор.Настройка.Значение = ПланыВидовХарактеристик.НастройкиПользователей.ОсновнаяОрганизация;
Записи.Отбор.Настройка.Использование = Истина;

Записи.Прочитать();

Подводные камни логики работы с НаборомЗаписей

Помимо неактивного отбора, в исходном коде автора скрываются две критические логические ошибки, о которых важно помнить:

  1. Создание новой записи. Если у пользователя ранее не было настройки в регистре, вызов Записи.Прочитать() вернет пустой набор (0 записей). Цикл Для Каждого Запись Из Записи просто не начнет выполняться, новая строка не создастся, и запись не зафиксируется в базе. Для добавления новой записи при пустом наборе необходимо вызвать Записи.Добавить() и заполнить все измерения и ресурсы.
  2. Опасность потери данных. Метод Записи.Записать() по умолчанию принимает параметр замещения Замещать = Истина. Если отбор не был включен, вызов Записать() перезапишет весь регистр содержимым текущего набора. Если набор окажется пустым, вся таблица регистра сведений будет полностью очищена.

Способ 3. Использование Менеджера Записи (Оптимальный для одной строки)

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

Разберем готовый и безопасный пример кода:


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

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

← На главную