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