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