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