Почему возникает ошибка «Несовместимые типы ВЫРАЗИТЬ» для параметра в запросе и как её решить

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с запросами в системе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда необходимо получить данные из реквизитов объекта, переданного в запрос через параметр. Казалось бы, логично использовать конструкцию ВЫБОР совместно с оператором ВЫРАЗИТЬ, чтобы определить тип ссылки и обратиться к нужным полям. Однако на практике это часто приводит к ошибке: «Несовместимые типы ВЫРАЗИТЬ». Рассмотрим подробнее, почему это происходит и какие существуют эффективные способы решения данной задачи.

Суть проблемы: почему ВЫБОР не спасает от ошибки

Разберем типичную ситуацию. Программист пишет запрос, в который передается параметр &Параметр. Этот параметр может содержать ссылку на Справочник.Товары или Справочник.Услуги. Код запроса выглядит примерно так:


ВЫБРАТЬ
    ВЫБОР
        КОГДА &Параметр ССЫЛКА Справочник.Товары 
            ТОГДА ВЫРАЗИТЬ(&Параметр КАК Справочник.Товары).Артикул
        КОГДА &Параметр ССЫЛКА Справочник.Услуги 
            ТОГДА ВЫРАЗИТЬ(&Параметр КАК Справочник.Услуги).КодУслуги
        ИНАЧЕ ""
    КОНЕЦ КАК ЗначениеРеквизита

Проблема заключается в том, что платформа 1С выполняет статическую проверку типов перед выполнением запроса. В отличие от обычного программного кода, где ветки условия ИНАЧЕ могут игнорироваться, парсер запросов анализирует весь текст целиком. Когда в параметр передается конкретная ссылка (например, типа Справочник.Товары), система видит попытку приведения этого конкретного типа к Справочнику.Услуги во второй ветке. Поскольку эти типы принципиально несовместимы, возникает ошибка компиляции запроса еще до момента его фактического исполнения.

Важно понимать: оператор ВЫРАЗИТЬ предназначен в первую очередь для работы с полями таблиц составного типа, описанными в метаданных. Для параметров, тип которых определяется в момент передачи значения, такая строгая проверка часто становится препятствием.

Способ 1: Использование временных таблиц с типизацией

Одним из самых надежных способов «обмануть» механизм жесткой типизации является использование временной таблицы. Мы можем создать структуру, где колонка будет иметь явно заданный составной тип. Проанализируем, как это сделать программно:

  1. Создадим ТаблицуЗначений и добавим в неё колонку с нужным ОписаниеТипов.
  2. Заполним таблицу значением нашего параметра.
  3. Поместим таблицу в МенеджерВременныхТаблиц.

// Программная подготовка
ТЗ = Новый ТаблицаЗначений;
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("СправочникСсылка.Товары"));
МассивТипов.Добавить(Тип("СправочникСсылка.Услуги"));
ОписаниеТипов = Новый ОписаниеТипов(МассивТипов);
ТЗ.Колонки.Добавить("СсылкаНаОбъект", ОписаниеТипов);

НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.СсылкаНаОбъект = ПараметрЗначение;

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТЗ", ТЗ);
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ";
Запрос.Выполнить();

Теперь в основном запросе мы можем обращаться к полю СсылкаНаОбъект таблицы ВТ_Данные. Так как поле в метаданных временной таблицы теперь составное, оператор ВЫРАЗИТЬ отработает корректно для любой ветки.

Способ 2: Левые соединения (SQL-подход)

Выясним, как решить задачу без использования ВЫРАЗИТЬ вообще. Наиболее производительным и «чистым» с точки зрения СУБД подходом является использование левых соединений. Мы присоединяем все возможные таблицы справочников к нашему параметру.


ВЫБРАТЬ
    ЕСТЬNULL(Товары.Артикул, ЕСТЬNULL(Услуги.КодУслуги, "")) КАК Результат
ИЗ
    (ВЫБРАТЬ &Параметр КАК Ссылка) КАК ИсходныеДанные
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Товары КАК Товары
    ПО ИсходныеДанные.Ссылка = Товары.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Услуги КАК Услуги
    ПО ИсходныеДанные.Ссылка = Услуги.Ссылка

В данном примере, если параметр является товаром, данные подтянутся из первой таблицы, а во второй будет NULL. Функция ЕСТЬNULL позволит выбрать первое непустое значение. Этот метод избавляет от проблем с типизацией параметров, так как сравнение ссылок разных типов в условии ПО разрешено и просто возвращает Ложь.

Способ 3: Предварительный анализ типа в коде

Если нам нужно получить только один реквизит, зачастую нет смысла усложнять запрос. Мы можем проанализировать тип значения в коде 1С и передать в запрос уже готовое значение или сформировать текст запроса динамически.

Посмотрим на пример динамического формирования:


ТекстПоля = " """" "; // По умолчанию пустая строка
Если ТипЗнч(Параметр) = Тип("СправочникСсылка.Товары") Тогда
    ТекстПоля = " Спр.Артикул ";
    ИмяТаблицы = "Справочник.Товары";
ИначеЕсли ТипЗнч(Параметр) = Тип("СправочникСсылка.Услуги") Тогда
    ТекстПоля = " Спр.КодУслуги ";
    ИмяТаблицы = "Справочник.Услуги";
КонецЕсли;

Запрос.Текст = "ВЫБРАТЬ " + ТекстПоля + " ИЗ " + ИмяТаблицы + " КАК Спр ГДЕ Спр.Ссылка = &Параметр";

Преимущество: Запрос получается максимально простым и быстрым для СУБД, так как отсутствуют лишние соединения и проверки типов внутри SQL-трансляции.

Способ 4: Использование ТИПЗНАЧЕНИЯ в условии

Если задача стоит в том, чтобы просто проверить тип внутри запроса (без обращения к реквизитам через точку), можно использовать функцию ТИПЗНАЧЕНИЯ. Она работает стабильнее, чем ВЫРАЗИТЬ, при сравнении с конкретным типом.


ВЫБРАТЬ
    ВЫБОР
        КОГДА ТИПЗНАЧЕНИЯ(&Параметр) = ТИП(Справочник.Товары) ТОГДА "Это товар"
        КОГДА ТИПЗНАЧЕНИЯ(&Параметр) = ТИП(Справочник.Услуги) ТОГДА "Это услуга"
        ИНАЧЕ "Неизвестно"
    КОНЕЦ

Однако помните: как только вы попытаетесь написать ВЫРАЗИТЬ(&Параметр КАК...).Поле, вы снова вернетесь к проблеме статической проверки типов.

Резюме и рекомендации

Подводя итог, можно выделить следующие правила работы с типизацией параметров в запросах:

  1. Избегайте ВЫРАЗИТЬ для параметров, если они не являются полями временной таблицы с явно заданным составным типом.
  2. Для получения реквизитов разных объектов по одной ссылке используйте ЛЕВОЕ СОЕДИНЕНИЕ с конкретными таблицами метаданных.
  3. Если типов очень много (например, более 5-10), лучше использовать динамическое формирование текста запроса, чтобы не перегружать план выполнения SQL лишними соединениями.
  4. Всегда проверяйте, нельзя ли получить нужное значение еще до передачи в запрос средствами встроенного языка — это сэкономит ресурсы сервера баз данных.

Используя эти подходы, мы обеспечим стабильную работу кода и отсутствие досадных ошибок типизации при выполнении сложных выборок данных.

← На главную