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