Фильтр по виду документа в языке запросов 1С: составные поля, регистраторы, параметры

· 9 мин

Содержание

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

Такая фильтрация нужна только для полей составного типа: регистратор в регистре, субконто «Документы расчётов», реквизит вроде ДокументОснование, строка журнала документов. Если вы выбираете из таблицы Документ.ПоступлениеТоваровУслуг, тип уже известен — ограничивать нечего. Всё ниже работает одинаково в 8.3 на файловой базе, MS SQL и PostgreSQL.

ССЫЛКА и ТИПЗНАЧЕНИЯ: два способа отфильтровать документы по типу

В языке запросов есть две конструкции для проверки типа. Результат у них одинаковый, разница в читаемости и в том, что можно подставить справа.

КонструкцияПримерКогда удобна
ССЫЛКАТ.Регистратор ССЫЛКА Документ.РеализацияТоваровУслугОдин-два вида документа, тип известен при написании запроса
ТИПЗНАЧЕНИЯ() = ТИП()ТИПЗНАЧЕНИЯ(Т.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг)Сравнение двух полей между собой или с параметром
ТИПЗНАЧЕНИЯ() В (&Параметр)ТИПЗНАЧЕНИЯ(Т.Регистратор) В (&ТипыДокументов)Список видов документов задаёт пользователь или код

Базовый запрос по движениям регистра накопления выглядит так:

ВЫБРАТЬ
    Т.Регистратор КАК Документ,
    Т.Номенклатура,
    Т.Количество
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Т
ГДЕ
    Т.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
    И Т.Период МЕЖДУ &НачалоПериода И &КонецПериода

Чтобы исключить вид документа, добавьте НЕ: НЕ Т.Регистратор ССЫЛКА Документ.ВводНачальныхОстатков. Несколько видов через ССЫЛКА соединяют связкой ИЛИ в скобках.

В тексте запроса пишут ТИП(Документ.Имя), а во встроенном языке — Тип("ДокументСсылка.Имя"). Это разные синтаксисы: перепутать их — частая причина ошибки при выполнении.

Ещё один полезный приём — проверка, что ссылка заполнена и при этом нужного вида: Т.ДокументОснование ССЫЛКА Документ.ЗаказКлиента И Т.ДокументОснование <> ЗНАЧЕНИЕ(Документ.ЗаказКлиента.ПустаяСсылка). Пустая ссылка тоже имеет тип, поэтому одно условие ССЫЛКА её не отсеет.

Оператор ВЫБОР: метки и ветвление по виду документа

ВЫБОР не фильтрует сам по себе, но решает две задачи, которые часто идут рядом с фильтром.

Первая — подпись вида документа в результате. Отчёту нужна колонка «Вид операции», а не служебный тип:

ВЫБОР
    КОГДА Т.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
        ТОГДА "Приход"
    КОГДА Т.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
        ТОГДА "Расход"
    ИНАЧЕ "Прочее"
КОНЕЦ КАК ВидОперации

Вторая — получить один и тот же реквизит у разных документов. У поступления и реализации есть Контрагент, но в составном поле обращаться к нему через точку дорого (подробнее в разделе про соединения). Правильно — привести тип внутри ветки:

ВЫБОР
    КОГДА Т.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
        ТОГДА ВЫРАЗИТЬ(Т.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Контрагент
    КОГДА Т.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
        ТОГДА ВЫРАЗИТЬ(Т.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент
КОНЕЦ КАК Контрагент

Для строк других видов поле вернёт NULL. Если это мешает, добавьте ветку ИНАЧЕ ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).

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

Регистраторы, субконто и поле «Тип» в виртуальных таблицах

Где именно ставить условие на тип, зависит от источника данных.

Физическая таблица движений регистра

РегистрНакопления.Имя без скобок и РегистрСведений.Имя (подчинённый регистратору) — это таблицы движений. Условие на Регистратор пишется в ГДЕ, как в примере выше. Это самый прямой путь, если нужны строки конкретных документов.

Виртуальные таблицы Остатки, Обороты, ОстаткиИОбороты

В параметр «Условие» виртуальной таблицы можно писать отборы по измерениям. Регистратор измерением не является, поэтому туда его не поставить. Варианты:

  • 📌 таблица Обороты с периодичностью Регистратор — поле Регистратор появится в выборке, фильтр по типу ставьте в ГДЕ внешнего запроса;
  • 📌 нужны только обороты конкретных документов — часто проще взять физическую таблицу движений и сгруппировать её самому;
  • 📌 нужны остатки — тип документа к ним неприменим, остаток не принадлежит одному регистратору.

Субконто регистра бухгалтерии

В регистре Хозрасчетный документ часто лежит в субконто (например, «Документы расчётов с контрагентами»). Субконто — измерение, поэтому условие на его тип допустимо прямо в параметрах виртуальной таблицы:

ВЫБРАТЬ
    О.Субконто1 КАК Контрагент,
    О.Субконто3 КАК ДокументРасчетов,
    О.СуммаКонечныйОстатокДт КАК Долг
ИЗ
    РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты(
        &НачалоПериода, &КонецПериода, , ,
        Счет В ИЕРАРХИИ (&Счет62),
        ,
        Субконто3 ССЫЛКА Документ.РеализацияТоваровУслуг) КАК О

Номер субконто зависит от счёта и настроек плана счетов. Проверьте его в карточке счёта, прежде чем копировать пример.

Тот же приём работает в ДвиженияССубконто, если нужен перечень проводок с документом нужного вида.

Журнал документов и его поле «Тип»

У таблицы журнала документов есть стандартное поле Тип — вид документа строки. Фильтровать по нему так же просто, как по регистратору:

ВЫБРАТЬ
    Ж.Ссылка,
    Ж.Дата,
    Ж.Номер
ИЗ
    ЖурналДокументов.СкладскиеДокументы КАК Ж
ГДЕ
    Ж.Тип = ТИП(Документ.ПеремещениеТоваров)

Имя журнала здесь условное: подставьте журнал из своей конфигурации. Если подходящего журнала нет, объедините таблицы нужных документов через ОБЪЕДИНИТЬ ВСЕ — это тоже ограничение по типу, только явное.

Составной тип в соединениях: ВЫРАЗИТЬ вместо разыменования

Главный приём при работе с документами в составных полях — не обращаться к реквизитам через точку без приведения типа.

Когда вы пишете Т.Регистратор.Склад, платформа не знает, какой документ в строке. Она строит соединение с таблицами всех документов, которые могут быть регистраторами этого регистра. В типовой конфигурации их бывают десятки. Запрос резко тяжелеет, а в профилировщике СУБД видна длинная цепочка LEFT JOIN.

Решение — сначала ограничить тип, потом привести поле:

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

Теперь соединение строится только с одной таблицей документа.

Явное соединение тоже допустимо:

ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Т
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Д
    ПО Т.Регистратор = Д.Ссылка

ВНУТРЕННЕЕ СОЕДИНЕНИЕ с таблицей конкретного документа само отсекает строки других видов: ссылка разного типа равенство не пройдёт. При ЛЕВОМ СОЕДИНЕНИИ строки других видов останутся, а поля Д в них будут NULL — тогда условие на тип нужно отдельно.

Если вы соединяете две таблицы по двум составным полям (Т1.Документ = Т2.Документ), а нужен только один вид документа, поставьте условие на тип у обеих сторон или хотя бы у той, что больше по объёму. Так СУБД отсечёт лишние строки до соединения.

Какой у вас случай?

Как передать типы документов параметром

Жёстко прописанный тип годится для разовой выборки. В обработках и отчётах список видов обычно приходит извне — из настроек, формы, константы.

Массив типов

Самый чистый способ — собрать массив значений Тип и передать его в запрос:

ТипыДокументов = Новый Массив;
ТипыДокументов.Добавить(Тип("ДокументСсылка.ПоступлениеТоваровУслуг"));
ТипыДокументов.Добавить(Тип("ДокументСсылка.РеализацияТоваровУслуг"));

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

Текст запроса не меняется, план выполнения СУБД может переиспользовать.

Тип по образцу ссылки

Если на руках уже есть ссылка нужного документа, тип берут из неё: ТИПЗНАЧЕНИЯ(Т.Регистратор) = ТИПЗНАЧЕНИЯ(&ОбразецСсылки). В параметр подойдёт и пустая ссылка: Документы.РеализацияТоваровУслуг.ПустаяСсылка().

Подстановка имени в текст запроса

Иногда виды документов хранятся строками (имена объектов метаданных). Тогда текст собирают динамически. Делайте это только через проверку имени по метаданным, иначе любая опечатка или чужая строка попадёт прямо в запрос:

ИмяДокумента = "РеализацияТоваровУслуг";
Если Метаданные.Документы.Найти(ИмяДокумента) = Неопределено Тогда
    ВызватьИсключение СтрШаблон("Нет документа с именем %1", ИмяДокумента);
КонецЕсли;
ТекстУсловия = "Т.Регистратор ССЫЛКА Документ." + ИмяДокумента;
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеПоТипу", ТекстУсловия);

Обычно проще превратить имя в тип — Тип("ДокументСсылка." + ИмяДокумента) — и пойти путём массива. Подстановка текста нужна, когда к типу привязано ещё и ВЫРАЗИТЬ с конкретными реквизитами.

Пустой массив в В (&ТипыДокументов) даёт пустой результат, а не «все документы». Если пустой список в вашей форме значит «без отбора», уберите условие из текста запроса программно.

Скорость: что ускоряет фильтр по типу, а что тормозит

Поле составного типа хранится в таблице СУБД несколькими колонками. Одна из них — код типа, другая — сама ссылка. Условие ССЫЛКА или ТИПЗНАЧЕНИЯ превращается в сравнение колонки с кодом типа. Это дёшево.

Дорого другое:

  • 🐢 разыменование Регистратор.Реквизит без ВЫРАЗИТЬ — соединения со всеми таблицами регистраторов;
  • 🐢 ВЫБОР или функции над полем в ГДЕ вместо простого условия;
  • 🐢 фильтр по ПРЕДСТАВЛЕНИЕ(Т.Регистратор) ПОДОБНО "Реализация%" — строковое сравнение по каждой строке, да ещё и с ошибками на переименованных документах;
  • 🐢 отбор по типу только во внешнем запросе, когда во вложенном уже собраны миллионы строк.

Что помогает:

  • ⚡ ставить условие на тип как можно раньше — в первом пакетном запросе, во временную таблицу класть уже отфильтрованные строки;
  • ⚡ сочетать тип с периодом: на больших регистрах именно период отсекает основную массу записей;
  • ⚡ индексировать временную таблицу (ИНДЕКСИРОВАТЬ ПО) по полю-ссылке, если дальше по ней идёт соединение;
  • ⚡ проверять план запроса: в клиент-серверной базе — через технологический журнал или профилировщик СУБД, в Конфигураторе — через замер производительности.

Ошибки при ограничении типа документа

Диагностика: выберите симптом

Ещё одна ловушка — сравнение с Неопределено. Составное поле может быть пустым, и условие НЕ Т.ДокументОснование ССЫЛКА Документ.Имя пропустит такие строки. Если пустые значения не нужны, отсекайте их отдельно: Т.ДокументОснование <> НЕОПРЕДЕЛЕНО.

Что зависит от версии платформы и режима работы

Операторы ССЫЛКА, ТИПЗНАЧЕНИЯ, ТИП, ВЫРАЗИТЬ и ВЫБОР есть и в 8.2, и в 8.3. Запрос, написанный под 8.2, по части фильтра по типу переносится в 8.3 без правок. Различия лежат в окружении.

ЧтоКак влияет
Управляемые формы и тонкий клиентОбъект Запрос на тонком клиенте недоступен. Запрос выполняют в серверной процедуре или общем модуле, массив типов формируют там же
СУБД (файловая, MS SQL, PostgreSQL)Результат одинаковый, план и время отличаются. На больших регистрах проверяйте план на той СУБД, где работает база
Расширения конфигурацииДокумент из расширения указывается по полному имени с префиксом, например ТИП(Документ.Расш1_АктСверки). Без активного расширения запрос упадёт
Система компоновки данныхВ тексте набора данных работают те же условия. Пользовательский отбор по виду документа удобно сделать через параметр-массив типов
Режим совместимостиНа эти операторы не влияет

В отчётах на СКД есть отдельный приём. Если пользователь должен выбирать вид документа в настройках, заведите параметр с типом «Тип» и разрешённым списком значений, а в запросе используйте ТИПЗНАЧЕНИЯ(Т.Регистратор) В (&ТипыДокументов). Это стабильнее, чем отбор по строковому представлению регистратора.

Частые вопросы

Чем ССЫЛКА отличается от ТИПЗНАЧЕНИЯ = ТИП?
По результату ничем. ССЫЛКА короче и читается проще, ТИПЗНАЧЕНИЯ нужен, когда справа параметр, другое поле или список типов.
Можно ли проверить, что ссылка — вообще документ, а не справочник?
Отдельного оператора «любой документ» в языке запросов нет. Перечислите нужные виды через ССЫЛКА или массив типов. Во встроенном языке проверка делается через Метаданные.Документы.Содержит(ссылка.Метаданные()).
Как вывести в отчёт название вида документа, не прописывая ВЫБОР?
Выберите в запросе ТИПЗНАЧЕНИЯ(Т.Регистратор), а при обработке результата получите синоним: Метаданные.НайтиПоТипу(Тип).Синоним. Для отчёта на СКД подойдёт ВЫБОР в запросе или вычисляемое поле.
Почему ВЫРАЗИТЬ к документу не ускорил запрос?
Скорее всего, рядом осталось обращение через точку без приведения — например, в СГРУППИРОВАТЬ ПО или в УПОРЯДОЧИТЬ ПО. Приводить нужно везде, где берётся реквизит.
Работает ли ограничение типа для реквизита табличной части?
Да. Если реквизит табличной части имеет составной тип, условие ССЫЛКА или ТИПЗНАЧЕНИЯ пишется так же, только поле берётся из таблицы табличной части.