При работе с отраслевыми решениями 1С (например, «1С:Производственная безопасность») программисты часто сталкиваются с ошибкой в динамических списках или обработках заполнения данных. Текст ошибки выглядит следующим образом: «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов». Ошибка локализуется в блоке КОГДА &ОПО <> ЗНАЧЕНИЕ(Справочник.ОпасныеПроизводственныеОбъекты.ПустаяСсылка).
В этой статье мы подробно разберем, почему возникает эта ситуация, проанализируем поведение платформы 1С и найдем радикальное решение, которое позволит избежать подобных сбоев в будущем.
Давайте выясним причину такого поведения системы. Основная проблема кроется в динамической типизации параметров запроса. В языке запросов 1С платформа пытается определить тип параметра &ОПО в момент выполнения. Если в программном коде в этот параметр передается одиночная ссылка (например, Справочники.ОпасныеПроизводственныеОбъекты.ПустаяСсылка()), запрос отрабатывает корректно.
Однако, если логика программы меняется (например, пользователь выбрал несколько объектов), и в параметр &ОПО передается Массив или Список значений, механизм компоновки данных или динамический список сталкивается с противоречием. С точки зрения SQL, невозможно выполнить операцию прямого сравнения <> (не равно) между коллекцией (массивом) и одиночным значением ссылки. Именно это вызывает исключение «поля несовместимых типов».
Ситуация осложняется тем, что использование функции ТИПЗНАЧЕНИЯ() в самом запросе применительно к параметрам (а не к полям таблиц базы данных) часто работает нестабильно или вовсе не поддерживается на уровне SQL-транслятора, что мы и видим в логах ошибки.
Рассмотрим пример кода, который чаще всего приводит к данной ошибке в «Помощнике заполнения отчета»:
ГДЕ
ГодовойПлан.Организация = &Организация
И ВЫБОР
КОГДА &ОПО <> ЗНАЧЕНИЕ(Справочник.прбОпасныеПроизводственныеОбъекты.ПустаяСсылка)
ТОГДА ГодовойПлан.ОПО В (&ОПО)
ИНАЧЕ ИСТИНА
КОНЕЦ
В данной конструкции программист пытается сделать «универсальный» фильтр. Если параметр заполнен, накладываем условие В (&ОПО), если нет — игнорируем фильтр. Но как только &ОПО становится массивом, первая строка условия КОГДА ломает выполнение запроса.
Рассмотрим наиболее правильный и радикальный способ устранения ошибки. Чтобы запрос всегда работал предсказуемо, мы должны гарантировать, что параметр &ОПО всегда имеет один и тот же тип — Массив.
Для этого в программном модуле, где устанавливаются параметры запроса (обычно это модуль формы или команда заполнения), необходимо выполнить следующую проверку:
// Вместо прямой передачи ссылки или массива делаем так:
МассивДляЗапроса = Новый Массив;
Если ТипЗнч(ЗначениеФильтра) = Тип("СправочникСсылка.прбОпасныеПроизводственныеОбъекты") Тогда
Если ЗначениеФильтра <> Справочники.прбОпасныеПроизводственныеОбъекты.ПустаяСсылка() Тогда
МассивДляЗапроса.Добавить(ЗначениеФильтра);
КонецЕсли;
ИначеЕсли ТипЗнч(ЗначениеФильтра) = Тип("Массив") Тогда
МассивДляЗапроса = ЗначениеФильтра;
КонецЕсли;
// Устанавливаем признак, нужно ли вообще фильтровать по ОПО
Запрос.УстановитьПараметр("ИспользоватьФильтрОПО", МассивДляЗапроса.Количество() > 0);
Запрос.УстановитьПараметр("ОПО", МассивДляЗапроса);
Теперь изменим сам текст запроса, чтобы он стал проще и надежнее:
ГДЕ
ГодовойПлан.Организация = &Организация
И (&ИспользоватьФильтрОПО = ЛОЖЬ ИЛИ ГодовойПлан.ОПО В (&ОПО))
Преимущества этого подхода: мы избавляемся от тяжелой конструкции ВЫБОР в секции ГДЕ, что положительно сказывается на производительности (SARGability запроса) и полностью исключает ошибки типов.
Если по каким-то причинам вы не можете изменить программный код формирования параметров и вынуждены править только текст запроса, можно использовать «хитрость» с оператором В. В языке запросов 1С оператор В корректно обрабатывает как одиночные значения, так и массивы.
Проанализируем исправленный вариант условия:
ГДЕ
ГодовойПлан.Организация = &Организация
И ВЫБОР
КОГДА НЕ ЗНАЧЕНИЕ(Справочник.прбОпасныеПроизводственныеОбъекты.ПустаяСсылка) В (&ОПО)
ТОГДА ГодовойПлан.ОПО В (&ОПО)
ИНАЧЕ ИСТИНА
КОНЕЦ
Важное примечание: Этот способ сработает, только если в &ОПО передается либо массив ссылок, либо одиночная пустая ссылка. Однако, если в массив попадет значение NULL, результат сравнения может быть непредсказуемым. Поэтому крайне желательно очищать массив от NULL перед передачей в запрос.
Если ошибка возникает в динамическом списке, который настроен через «Произвольный запрос», обратите внимание на основную таблицу. Если в параметрах основной таблицы установлены несовместимые типы, платформа может выдавать ошибку еще на этапе генерации текста SQL-запроса для СУБД.
Разберем шаги по настройке динамического списка:
Объект.Список.Параметры.УстановитьЗначениеПараметра() для явного указания типа.При реализации исправлений важно помнить о нескольких правилах работы с коллекциями в параметрах запроса:
Массив.Удалить(), чтобы исключить значения NULL. В запросе условие Поле В (&Массив) вернет Ложь для строк, где Поле равно NULL, даже если в массиве есть NULL.&ОПО передать совершенно пустой массив, условие Поле В (&ОПО) всегда будет Ложь. Поэтому всегда используйте дополнительный булев флаг (например, &ОтборПоОПОВключен).Подводя итог, можно сказать, что «радикальное» устранение ошибки заключается в отказе от передачи разнородных типов в один и тот же параметр. Переход на использование массивов как универсального контейнера для фильтров — это стандарт разработки на платформе 1С, который делает ваш код надежным и устойчивым к изменениям данных в базе.