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