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