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