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

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

При разработке сложных отчетов или обработок программисты 1С часто сталкиваются с необходимостью фильтровать данные по определенному типу документа. Типичная попытка передать тип напрямую через Запрос.УстановитьПараметр("ТипДок", Тип("Документ.ПриходнаяНакладная")) и использовать его в операторе ССЫЛКА заканчивается ошибкой. Это происходит потому, что оператор ССЫЛКА в языке запросов 1С является статической конструкцией — он ожидает имя метаданных сразу в тексте запроса, а не значение из параметра.

В этой статье мы подробно разберем, почему стандартный подход не работает, и проанализируем несколько проверенных способов решения этой задачи: от динамической подстановки текста до использования особенностей оператора ТИПЗНАЧЕНИЯ.

Причина ограничения: почему ССЫЛКА не принимает параметры?

Прежде чем переходить к решениям, разберем ситуацию. Язык запросов 1С транслируется в SQL-запрос. Оператор ССЫЛКА преобразуется в проверку конкретного идентификатора таблицы в поле ссылки (составного типа). Поскольку структура таблиц в базе данных известна заранее, платформа оптимизирует этот запрос, подставляя жесткие ссылки на таблицы. Параметризация этого процесса на уровне стандартного УстановитьПараметр не предусмотрена архитектурой.

Способ 1. Динамическое формирование текста запроса

Это самый распространенный и гибкий метод. Мы создаем шаблон запроса и программно подставляем в него имя нужного метаданного. Рассмотрим, как это сделать правильно, чтобы не "сломать" конструктор запросов.

Для реализации этого метода воспользуемся функцией СтрЗаменить или СтрШаблон. Проанализируем пример кода:


// Определяем имя документа программно
ИмяДокумента = "";
Если ТипЗнч(Накладная) = Тип("ДокументСсылка.ПриходнаяНакладная") Тогда
    ИмяДокумента = "ПриходнаяНакладная";
Иначе
    ИмяДокумента = "РасходнаяНакладная";
КонецЕсли;

ТекстЗапроса = 
"ВЫБРАТЬ
|   Остатки.Регистратор КАК Регистратор,
|   Остатки.Количество КАК Количество
|ИЗ
|   РегистрНакопления.ОстаткиНоменклатуры КАК Остатки
|ГДЕ
|   Остатки.Регистратор ССЫЛКА Документ.&ИмяДокумента";

// Заменяем маркер в тексте на реальное имя метаданных
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИмяДокумента", ИмяДокумента);

Запрос = Новый Запрос(ТекстЗапроса);
Результат = Запрос.Выполнить();

Важный нюанс: использование символа & внутри текста (как в примере &ИмяДокумента) может сбивать с толку конструктор запросов, так как он воспринимает это как стандартный параметр. Чтобы конструктор продолжал работать, рекомендуется писать в запросе реально существующий тип (например, Документ.ПриходнаяНакладная), а затем программно заменять эту строку целиком.

Способ 2. Использование оператора ТИПЗНАЧЕНИЯ с пустой ссылкой

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

Разберем этот элегантный способ по шагам:


// Создаем пустую ссылку нужного типа
ПустаяСсылка = Документы.ПриходнаяНакладная.ПустаяСсылка();

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

// Передаем пустую ссылку в параметр
Запрос.УстановитьПараметр("ПараметрТипа", ПустаяСсылка);
Результат = Запрос.Выполнить();

Почему это работает? В языке запросов функция ТИПЗНАЧЕНИЯ возвращает внутренний идентификатор типа. Когда мы сравниваем ТИПЗНАЧЕНИЯ(Поле) и ТИПЗНАЧЕНИЯ(&Параметр), платформа корректно сопоставляет их на уровне SQL. Этот метод хорош тем, что текст запроса остается статичным, и конструктор запросов работает без проблем.

Способ 3. Выбор типа через временную таблицу

Если нам нужно фильтровать данные сразу по нескольким типам или мы хотим получить тип из имеющегося объекта, можно использовать временную таблицу. Рассмотрим ситуацию, когда у нас есть ссылка на документ, и мы хотим выбрать все записи того же типа, что и эта ссылка.


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|   ТИПЗНАЧЕНИЯ(&ОбразецСсылки) КАК ТипДокумента
|ПОМЕСТИТЬ ВТ_Типы
|;
|ВЫБРАТЬ
|   Остатки.Регистратор КАК Регистратор
|ИЗ
|   РегистрНакопления.ОстаткиНоменклатуры КАК Остатки
|ГДЕ
|   ТИПЗНАЧЕНИЯ(Остатки.Регистратор) В (ВЫБРАТЬ ТипДокумента ИЗ ВТ_Типы)";

Запрос.УстановитьПараметр("ОбразецСсылки", ДокументСсылка);
Результат = Запрос.Выполнить();

Этот метод полезен в универсальных механизмах, где заранее неизвестно, какой объект придет на вход. Мы "извлекаем" тип из образца прямо внутри SQL-контекста.

Способ 4. Использование логических условий (Варианты запроса)

Если количество возможных типов документов невелико и заранее известно (например, только Приходная и Расходная накладные), можно прописать все условия в одном запросе и управлять их включением через числовой параметр.


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|   Регистратор
|ИЗ
|   РегистрНакопления.ОстаткиНоменклатуры
|ГДЕ
|   (&Вариант = 1 И Регистратор ССЫЛКА Документ.ПриходнаяНакладная)
|   ИЛИ (&Вариант = 2 И Регистратор ССЫЛКА Документ.РасходнаяНакладная)";

// Выбираем логику отбора
Запрос.УстановитьПараметр("Вариант", ?(ЭтоПриход, 1, 2));

Такой подход максимально производителен, так как платформа использует оператор ССЫЛКА, который работает быстрее, чем ТИПЗНАЧЕНИЯ, за счет прямой проверки идентификатора таблицы в SQL-запросе.

Что выбрать?

Проанализируем наши методы и выберем лучший для конкретных ситуаций:

  1. Динамическая подстановка (Способ 1): Лучший выбор для универсальных отчетов, где типов может быть очень много.
  2. Пустая ссылка (Способ 2): Самый удобный и "чистый" способ для повседневных задач, сохраняющий работоспособность конструктора.
  3. Логические условия (Способ 4): Рекомендуется для высоконагруженных систем, где критична скорость выполнения запроса, а количество типов ограничено.

Помните, что использование оператора ССЫЛКА всегда предпочтительнее с точки зрения производительности SQL, так как ТИПЗНАЧЕНИЯ требует дополнительных вычислительных ресурсов сервера базы данных для определения типа в каждой строке выборки.

← На главную