Как правильно отобрать значение по типу или предопределенному элементу в запросе 1С?

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

При работе с запросами в платформе 1С:Предприятие разработчики часто сталкиваются с необходимостью отфильтровать данные по конкретному типу или предопределенному значению. На первый взгляд задачи кажутся похожими, однако использование неверных инструментов приводит к досадным ошибкам, таким как «Нельзя сравнивать поля неограниченной длины и несовместимых типов». В этой статье мы подробно разберем, почему возникает эта путаница, детально изучим разницу между схожими механизмами платформы и рассмотрим правильные варианты решения задачи.

Разбор проблемы: в чем запутался автор темы?

Проанализируем ситуацию, описанную на форуме. У автора есть табличная часть или таблица базы данных, в которой присутствует поле ТипДокумента. Данное поле имеет тип ссылки на справочник СправочникСсылка.ТипыДокументов. В этом справочнике создано несколько предопределенных элементов, одним из которых является СвидетельствоОбАккредитацииСпециалиста.

Автор пытается наложить условие в запросе, чтобы выбрать строки, где значением поля является именно это свидетельство. При попытке написать условие вида ГДЕ ТипДокумента = ТИП(...) или использовать неверную адресацию предопределенного элемента, система выдает ошибку. Это происходит из-за смешения понятий «Тип данных» (описание структуры данных в метаданных) и «Значение» (конкретный элемент справочника, хранящийся в базе данных).

Четыре ключевых механизма работы с типами и значениями

Для того чтобы раз и навсегда исключить подобные ошибки, подробно рассмотрим четыре важнейших инструмента языка запросов 1С. Выясним, для чего предназначен каждый из них и в каких сценариях применяется.

  1. Конструкция ТИП()

    Это литерал типа данных. Он возвращает сам тип как объект метаданных (например, тип справочника, документа или числа), а не конкретную запись в базе данных. Внутри круглых скобок указывается только имя объекта метаданных из конфигуратора.

    Важно: Внутри ТИП() нельзя обращаться к конкретным элементам справочников, перечислениям или табличным частям. Допустимый синтаксис: ТИП(Справочник.Номенклатура) или ТИП(Документ.ЗаказПокупателя).

  2. Функция ТИПЗНАЧЕНИЯ()

    Эта функция извлекает тип конкретного значения из выбранного поля прямо во время выполнения запроса СУБД. Чаще всего она применяется для анализа полей составного типа данных.

    Пример использования: Если поле ДокументОснование может быть и «Заказом», и «Реализацией», мы можем проверить его тип следующим образом:

    ГДЕ ТИПЗНАЧЕНИЯ(Таблица.ДокументОснование) = ТИП(Документ.ЗаказПокупателя)
  3. Логический оператор ССЫЛКА

    Это более быстрый, современный и лаконичный аналог связки функций ТИПЗНАЧЕНИЯ() и ТИП(). Он проверяет, принадлежит ли ссылка в поле базы данных к указанному типу метаданных.

    Пример использования: Предыдущий запрос проверки типа документа-основания гораздо проще и правильнее написать так:

    ГДЕ Таблица.ДокументОснование ССЫЛКА Документ.ЗаказПокупателя
  4. Функция ЗНАЧЕНИЕ()

    Данная функция позволяет получить прямую ссылку на конкретный предопределенный элемент справочника, значение перечисления, точку маршрута бизнес-процесса или пустую ссылку определенного типа. В отличие от ТИП(), она возвращает не абстрактный тип данных, а конкретную ссылку на запись в базе.

    Пример использования: Обращение к предопределенному элементу справочника:

    ГДЕ Таблица.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Согласован)

Выясняем причину ошибки «Несовместимые типы»

Давайте проанализируем, почему у автора темы возникла ошибка при попытке выполнить запрос. Автор пытался написать конструкцию вида: ГДЕ ТипДокумента = ТИП(Справочник.ТипыДокументов) или сравнивал ссылку с типом данных.

С точки зрения СУБД, поле ТипДокумента хранит уникальный идентификатор (GUID) конкретной записи справочника. Функция ТИП() возвращает системный идентификатор типа метаданных. Попытка сравнить GUID записи с системным идентификатором типа данных приводит к конфликту типов на уровне СУБД — программа физически не может сравнить эти сущности между собой, о чем и сообщает пользователю.

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

ВЫРАЗИТЬ(Таблица.Описание КАК СТРОКА(150))

Пошаговые варианты решения задачи

Рассмотрим подробнее два наиболее эффективных способа решения задачи, в зависимости от архитектуры вашей конфигурации.

Решение 1. Использование функции ЗНАЧЕНИЕ() для предопределенных элементов

Если поле ТипДокумента ссылается на справочник ТипыДокументов, а искомый элемент СвидетельствоОбАккредитацииСпециалиста является предопределенным в этом справочнике, то самым простым решением будет прямое указание этого значения в тексте запроса.

Разберем пример построения такого запроса по шагам:


ВЫБРАТЬ
    ЛичноеДело.Ссылка КАК Абитуриент,
    ЛичноеДело.ФизЛицо,
    ПринятыеДокументы.ТипДокумента КАК ТипДокумента
ИЗ
    Справочник.ЛичныеДела КАК ЛичноеДело
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ЛичныеДела.ПринятыеДокументы КАК ПринятыеДокументы
    ПО ПринятыеДокументы.Ссылка = ЛичноеДело.Ссылка
ГДЕ
    ПринятыеДокументы.ТипДокумента = ЗНАЧЕНИЕ(Справочник.ТипыДокументов.СвидетельствоОбАккредитацииСпециалиста)

В данном случае платформа 1С самостоятельно подставит нужную ссылку на предопределенный элемент при компиляции запроса, и СУБД успешно вернет только строки со свидетельствами.

Решение 2. Передача предопределенного значения через параметр (Best Practice)

Хотя жесткое прописывание путей через функцию ЗНАЧЕНИЕ() работает отлично, в профессиональной разработке хорошим тоном считается передача значений через параметры запроса. Это делает код более гибким и защищает от ошибок при возможном переименовании предопределенных элементов в конфигураторе.

Посмотрим на пример реализации данного подхода в коде:


// 1. Создаем объект запроса
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ПринятыеДокументы.Ссылка КАК ДокументСсылка,
    |	ПринятыеДокументы.ТипДокумента КАК ТипДокумента
    |ИЗ
    |	РегистрСведений.ПринятыеДокументы КАК ПринятыеДокументы
    |ГДЕ
    |	ПринятыеДокументы.ТипДокумента = &ЦелевойТипДокумента";

// 2. Получаем предопределенный элемент из базы данных и передаем в параметр
ПредопределенныйЭлемент = Справочники.ТипыДокументов.СвидетельствоОбАккредитацииСпециалиста;
Запрос.УстановитьПараметр("ЦелевойТипДокумента", ПредопределенныйЭлемент);

// 3. Выполняем запрос
РезультатЗапроса = Запрос.Выполнить();

Такой подход делает ваш код более читаемым, упрощает его отладку и позволяет при необходимости легко заменить отбор на другое значение без изменения самого текста запроса.

Резюме

Для успешного решения подобных задач всегда четко разделяйте сущности: если вам нужно проверить, к какому классу объектов (документ, справочник) относится запись составного типа, используйте оператор ССЫЛКА. Если же вам нужно отфильтровать таблицу по конкретному элементу справочника или значению перечисления — используйте функцию ЗНАЧЕНИЕ() либо передавайте конкретную ссылку через параметры запроса.

← На главную