Почему в запросе 1С возникает ошибка «Неверные параметры в операции сравнения» и как правильно получить данные из регистра сведений?

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

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

Разбор технической ошибки: причины и следствия

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

Проблема кроется в способе передачи аргумента. Когда мы пишем Для каждого СтрокаСотрудника Из ЭтотОбъект.СписокСотрудников Цикл, переменная СтрокаСотрудника представляет собой объект типа СтрокаТабличнойЧасти (или ДанныеФормыЭлементКоллекции). Этот объект является «контейнером», содержащим все данные текущей строки: сотрудника, подразделение, ставку и другие реквизиты.

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

Решение проблемы: правильная передача ссылки

Для исправления ошибки нам необходимо передать в функцию не весь объект строки, а конкретное значение реквизита «Сотрудник», которое и является ссылкой. Рассмотрим, как должен выглядеть исправленный вызов в модуле документа:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    Для каждого СтрокаСотрудника Из ЭтотОбъект.СписокСотрудников Цикл
        // Передаем именно поле .Сотрудник, а не всю переменную СтрокаСотрудника
        СтрокаСотрудника.СтавкаЧас = РегистрыСведений.ОкладыСотрудников.ПолучитьОклад(СтрокаСотрудника.Сотрудник, ЭтотОбъект.Дата);
    КонецЦикла;
    
КонецПроцедуры

После такого изменения тип параметра в запросе станет СправочникСсылка, и операция сравнения в условии виртуальной таблицы СрезПоследних отработает корректно. Это самый быстрый способ устранить ошибку «в моменте».

Использование стандартных методов платформы

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

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


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

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

Оптимизация производительности: долой запросы в цикле!

Теперь разберем самую важную архитектурную проблему исходного кода — запрос в цикле. В примере автора функция получения оклада вызывается для каждой строки табличной части. Если в документе будет 100 или 1000 сотрудников, система выполнит 100 или 1000 отдельных обращений к базе данных.

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

  1. Выгрузим всех уникальных сотрудников из табличной части в массив.
  2. Передадим этот массив в один запрос.
  3. Результат запроса поместим в соответствие для быстрого доступа в цикле.

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

КонецПроцедуры

Почему это важно?

Использование одного запроса вместо множества маленьких обращений — это золотой стандарт разработки на платформе 1С. Посмотрим на преимущества:

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

← На главную