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