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

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

При разработке отчетов и обработок в 1С часто возникает задача: выбрать документы, ссылочный реквизит которых удовлетворяет определенному условию из связанного справочника — например, элемент не должен быть помечен на удаление или должен входить в определенный список. Начинающие разработчики нередко разбивают такую операцию на несколько отдельных обращений к базе данных или испытывают сложности с передачей результата одной выборки в условие другой. Разберем по шагам несколько эффективных способов решения этой задачи в рамках одного пакетного запроса, а также проанализируем нюансы производительности каждого подхода.

Способ 1. Использование временной таблицы и конструкции «В (ВЫБРАТЬ ...)»

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

Посмотрим на пример пакетного запроса:


ВЫБРАТЬ
    Кос_ТипыПоступления.Ссылка КАК Ссылка
ПОМЕСТИТЬ СписокТипов
ИЗ
    Справочник.Кос_ТипыПоступления КАК Кос_ТипыПоступления
ГДЕ
    НЕ Кос_ТипыПоступления.ПометкаУдаления
ИНДЕКСИРОВАТЬ ПО
    Ссылка
;

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

////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ СписокТипов

Проанализируем ключевые моменты этой конструкции:

  1. Индексация временной таблицы: обратим внимание на директиву ИНДЕКСИРОВАТЬ ПО Ссылка. При росте объема данных в справочнике отсутствие индекса заставит СУБД выполнять полное сканирование временной таблицы для каждой проверяемой строки документа. Наличие индекса гарантирует быстрый поиск по временной таблице.
  2. Очистка ресурсов: в конце пакета всегда рекомендуется использовать инструкцию УНИЧТОЖИТЬ для освобождения памяти и табличного пространства СУБД сразу после выполнения запроса.
  3. Подзапрос во временную таблицу: оператор В (ВЫБРАТЬ ...) работает стабильно и предсказуемо именно тогда, когда во вложенной выборке используется предварительно подготовленная временная таблица, а не реальная таблица базы данных.

Способ 2. Использование явного внутреннего соединения

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


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

Выясним причину эффективности этого метода:

  1. При использовании ВНУТРЕННЕГО СОЕДИНЕНИЯ платформа отсекает строки документов, у которых реквизит Кос_ТипПоступления не заполнен, содержит значение NULL или ссылается на отсутствующую запись.
  2. Фильтрация НЕ Кос_ТипыПоступления.ПометкаУдаления в секции ГДЕ дополнительно отсекает документы с удаленными типами поступления. Оптимизатор СУБД объединяет эти условия в оптимальный план выполнения запроса.

Способ 3. Разыменование полей через точку и его подводные камни

Самый простой способ отбора, который часто используют разработчики, — прямое обращение к реквизиту связанной таблицы через точку в условии ГДЕ:


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

Разберем подробнее, как этот код воспринимается платформой и СУБД:

  1. Неявное левое соединение: конструкция Таблица.Реквизит.Свойство транслируется платформой 1С в СУБД как LEFT OUTER JOIN. Однако проверка условия на равенство или отрицание булевого поля автоматически отсекает строки, где связанное поле равно NULL, фактически сводя соединение к внутреннему.
  2. Опасность составных типов данных: если реквизит документа имеет составной тип (например, может принимать значения нескольких видов справочников или документов), обращение через точку заставит платформу выполнить неявное левое соединение со всеми таблицами, входящими в этот тип. Это приводит к резкому падению скорости выполнения запроса.
  3. Решение для составного типа: если избежать разыменования нельзя, обязательно ограничиваем тип с помощью функции ВЫРАЗИТЬ:
    
    ГДЕ НЕ ВЫРАЗИТЬ(ПоступлениеТоваровУслуг.Кос_ТипПоступления КАК Справочник.Кос_ТипыПоступления).ПометкаУдаления
    

Проверка ссылочных полей на пустое значение без использования параметров

В процессе модификации условий часто требуется проверить, заполнен ли реквизит документа, без передачи пустой ссылки через отдельный параметр. Для этого в языке запросов 1С предусмотрена специальная литеральная конструкция ЗНАЧЕНИЕ.

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


ГДЕ
    ПоступлениеТоваровУслуг.Кос_ТипПоступления <> ЗНАЧЕНИЕ(Справочник.Кос_ТипыПоступления.ПустаяСсылка)

Важно помнить: выражение ЗНАЧЕНИЕ(...) возвращает пустую ссылку конкретного типа метаданных. Если поле в результате левого соединения содержит NULL, условие проверки на пустую ссылку вернет ЛОЖЬ. Для комплексной проверки на заполненность используем совместное условие:


ГДЕ
    ПоступлениеТоваровУслуг.Кос_ТипПоступления ЕСТЬ НЕ NULL
    И ПоступлениеТоваровУслуг.Кос_ТипПоступления <> ЗНАЧЕНИЕ(Справочник.Кос_ТипыПоступления.ПустаяСсылка)

Оптимизация условий по периоду документа

При отборе документов по реквизиту Дата всегда учитываем, что поле содержит не только дату, но и время. Конструкция ПоступлениеТоваровУслуг.Дата МЕЖДУ &ДатаНач И &ДатаКон включает обе граничные точки.

Если в параметр &ДатаКон передается дата без указания времени (например, 01.05.2024 00:00:00), то документы, проведенные в течение дня 1 мая (например, в 14:30), не попадут в выборку. Чтобы избежать ошибок, в коде модуля перед установкой параметра обязательно приводим дату к концу периода с помощью функции КонецДня(&ДатаКон) либо задаем условие строгого полуинтервала:


ГДЕ
    ПоступлениеТоваровУслуг.Дата >= &ДатаНач
    И ПоступлениеТоваровУслуг.Дата < &ДатаКонВходящая

Поле Дата у документов входит в кластерный индекс таблицы, поэтому корректно заданное условие по периоду позволяет СУБД быстро сократить объем сканируемых строк перед выполнением любых соединений и условий.

← На главную