Почему запрос к срезу последних регистра сведений работает в консоли, но выдает ошибку в коде 1С?

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

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

Причина 1. Ошибочное обращение к результату запроса без позиционирования выборки

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

Посмотрим на ошибочный фрагмент кода:


РезультатЗапроса = Запрос.Выполнить();
ФизЛицо = РезультатЗапроса.Выбрать();
// Ошибка: "Поле объекта не обнаружено" или ошибка чтения значения
СотрудникСсылка = ФизЛицо.Сотрудник;

Для корректного получения данных необходимо обязательно вызвать метод Следующий() в цикле или через проверку условия:


РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Если Выборка.Следующий() Тогда
    СотрудникСсылка = Выборка.Сотрудник;
    ПериодЗаписи = Выборка.Период;
КонецЕсли;

Причина 2. Неправильное наложение условий на виртуальную таблицу «СрезПоследних»

Рассмотрим устройство виртуальной таблицы РегистрСведений.РаботникиОрганизаций.СрезПоследних. Распространенная ошибка заключается в указании условий по измерениям и реквизитам в секции ГДЕ, а не в параметрах самой виртуальной таблицы.

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

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

  1. Получаем ссылку на объект заранее: Сначала выполняем поиск физического лица или сотрудника по строке (наименованию из файла).
  2. Передаем ссылку в параметры виртуальной таблицы: Ограничиваем срез на уровне выборки измерений, а не пост-фильтрацией.

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


// 1. Находим ссылку на физлицо по строке
ФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию(СокрЛП(ФИОИзФайла), Истина);

Если ЗначениеЗаполнено(ФизЛицоСсылка) Тогда
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	РаботникиОрганизацийСрезПоследних.Сотрудник КАК Сотрудник,
    |	РаботникиОрганизацийСрезПоследних.Период КАК Период,
    |	РаботникиОрганизацийСрезПоследних.Регистратор КАК Регистратор
    |ИЗ
    |	РегистрСведений.РаботникиОрганизаций.СрезПоследних(
    |			&ПериодСреза,
    |			Сотрудник.Физлицо = &ФизЛицо
    |				И Сотрудник.ВидДоговора = &ВидДоговора
    |				И Сотрудник.ВидЗанятости В (&ВидыЗанятости)) КАК РаботникиОрганизацийСрезПоследних";
    
    Запрос.УстановитьПараметр("ПериодСреза", КонецДня(ТекущаяДата()));
    Запрос.УстановитьПараметр("ФизЛицо", ФизЛицоСсылка);
    Запрос.УстановитьПараметр("ВидДоговора", Перечисления.ВидыДоговоровСФизЛицами.ТрудовойДоговор);
    
    МассивЗанятости = Новый Массив;
    МассивЗанятости.Добавить(Перечисления.ВидыЗанятостиВОрганизации.ОсновноеМестоРаботы);
    МассивЗанятости.Добавить(Перечисления.ВидыЗанятостиВОрганизации.Совместительство);
    Запрос.УстановитьПараметр("ВидыЗанятости", МассивЗанятости);
    
    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();
    
    Пока Выборка.Следующий() Цикл
        // Обработка полученных данных
    КонецЦикла;
КонецЕсли;

Причина 3. Скрытые неразрывные пробелы при загрузке из Excel

Выясним причину, почему точное строковое совпадение может не срабатывать при передаче параметров из внешних таблиц. При чтении текстовых ячеек из Excel строки часто содержат неразрывные пробелы (символ с кодом 160). Функция СокрЛП() удаляет исключительно стандартные пробелы (код 32).

Если в базе ФИО записано обычными пробелами, а из Excel пришла строка с неразрывными пробелами, сравнение через =, В или метод НайтиПоНаименованию() вернет пустую ссылку.

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


СимволНПП = Символ(160);
ОчищенноеФИО = СтрЗаменить(ФИОИзФайла, СимволНПП, " ");
ОчищенноеФИО = СокрЛП(ОчищенноеФИО);

Причина 4. Избыточные конструкции группировки и сортировки

В исходном запросе использовались конструкции ВЫБРАТЬ ПЕРВЫЕ 1, СГРУППИРОВАТЬ ПО и УПОРЯДОЧИТЬ ПО Период УБЫВ. Виртуальная таблица СрезПоследних сама по себе гарантирует получение самой актуальной записи на указанную дату среза для каждой уникальной комбинации измерений. Дополнительная группировка и сортировка по периоду создают лишнюю нагрузку на СУБД и усложняют план выполнения запроса.

Итог: Чтобы избежать ошибок при кадровых выборках, необходимо:

  1. Всегда позиционировать выборку результата запроса с помощью Выборка.Следующий() перед чтением полей.
  2. Условия фильтрации накладывать во входных параметрах виртуальной таблицы СрезПоследних, а не в секции ГДЕ.
  3. Очищать входящие строковые данные из внешних источников от неразрывных пробелов и спецсимволов.
  4. Передавать в параметры запроса ссылки на объекты метаданных вместо поиска через разыменование полей в тексте запроса.
← На главную