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

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

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

Анализируем корректность установки отбора

Первая и самая распространенная причина пустого набора — банальные опечатки в именах свойств метаданных. В платформе 1С:Предприятие 8 для регистра сведений, подчиненного регистратору, системное поле отбора называется Регистратор (в единственном числе). Рассмотрим фрагмент кода из обсуждения:


НаборЗаписей.Отбор.Регистраторы.Установить(Регистраторы.Регистратор); // Ошибка!

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


НаборЗаписей = РегистрыСведений.СведенияОСтудентах.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ТекущийРегистратор);
НаборЗаписей.Прочитать();

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

Проверяем типы данных в отборе

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

  1. Объект вместо ссылки: В отбор по регистратору необходимо передавать ссылку (ДокументСсылка), а не сам объект документа (ДокументОбъект). Если вы только что создали документ в коде и еще не записали его, у него еще нет ссылки, и поиск по нему в регистре ничего не даст.
  2. Битые ссылки: Если в переменной содержится ссылка на удаленный объект ("Объект не обнаружен"), поиск по ней всегда будет возвращать пустой результат.
  3. Составные типы: Если поле в запросе или выборке имеет составной тип, убедитесь, что в него попало именно то значение, которое ожидается. Иногда полезно выполнить явное приведение типов с помощью функции ВЫРАЗИТЬ в запросе.

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


Если Документы.КорректировкаРегистров.ТипВсеСсылки().СодержитТип(ТипЗнч(Регистраторы.Регистратор)) Тогда
    НаборЗаписей.Отбор.Регистратор.Установить(Регистраторы.Регистратор);
Иначе
    Сообщить("Неверный тип регистратора!");
КонецЕсли;

Разбираем ловушку удаления записей в цикле

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


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

В чем заключается проблема? Когда мы вызываем метод Удалить() внутри цикла Для Каждого, коллекция изменяется. Индексы всех последующих элементов сдвигаются на единицу вверх. Итератор цикла, переходя к "следующему" элементу, фактически перескакивает через одну запись. В результате часть записей, подлежащих удалению, просто не будет проверена.

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


КолличествоЗаписей = НаборЗаписей.Количество();
Для Счетчик = 1 По КолличествоЗаписей Цикл
    ОбратныйИндекс = КолличествоЗаписей - Счетчик;
    Запись = НаборЗаписей[ОбратныйИндекс];
    
    Если Запись.Студент = НужныйСтудент Тогда
        НаборЗаписей.Удалить(ОбратныйИндекс);
    КонецЕсли;
КонецЦикла;

При таком подходе удаление текущего элемента не влияет на индексы тех элементов, которые нам еще только предстоит обработать (так как они находятся "выше" по списку).

Особенности работы с документом «Корректировка регистров»

В контексте обсуждения упоминался тип документа КорректировкаРегистров. Это специфический объект, предназначенный для ручного или программного исправления движений. Выясним нюансы работы с ним:

Рекомендации по оптимизации и безопасности

Разберем, как сделать работу с регистрами более надежной и производительной:

1. Использование временных массивов. Вместо удаления записей напрямую в цикле, можно сначала собрать ссылки на объекты, которые нужно удалить, в массив, а затем пройти по этому массиву и удалить соответствующие записи из набора. Это делает код более читаемым.

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

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

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

← На главную